mirror of
https://github.com/adulau/aha.git
synced 2024-12-26 18:56:14 +00:00
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (90 commits) jffs2: Fix long-standing bug with symlink garbage collection. mtd: OneNAND: Fix test of unsigned in onenand_otp_walk() mtd: cfi_cmdset_0002, fix lock imbalance Revert "mtd: move mxcnd_remove to .exit.text" mtd: m25p80: add support for Macronix MX25L4005A kmsg_dump: fix build for CONFIG_PRINTK=n mtd: nandsim: add support for 4KiB pages mtd: mtdoops: refactor as a kmsg_dumper mtd: mtdoops: make record size configurable mtd: mtdoops: limit the maximum mtd partition size mtd: mtdoops: keep track of used/unused pages in an array mtd: mtdoops: several minor cleanups core: Add kernel message dumper to call on oopses and panics mtd: add ARM pismo support mtd: pxa3xx_nand: Fix PIO data transfer mtd: nand: fix multi-chip suspend problem mtd: add support for switching old SST chips into QRY mode mtd: fix M29W800D dev_id and uaddr mtd: don't use PF_MEMALLOC mtd: Add bad block table overrides to Davinci NAND driver ... Fixed up conflicts (mostly trivial) in drivers/mtd/devices/m25p80.c drivers/mtd/maps/pcmciamtd.c drivers/mtd/nand/pxa3xx_nand.c kernel/printk.c
This commit is contained in:
commit
60d9aa758c
61 changed files with 3628 additions and 1758 deletions
|
@ -70,9 +70,19 @@ static struct ctl_table bcmring_sysctl_reboot[] = {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct resource nand_resource[] = {
|
||||||
|
[0] = {
|
||||||
|
.start = MM_ADDR_IO_NAND,
|
||||||
|
.end = MM_ADDR_IO_NAND + 0x1000 - 1,
|
||||||
|
.flags = IORESOURCE_MEM,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_device nand_device = {
|
static struct platform_device nand_device = {
|
||||||
.name = "bcm-nand",
|
.name = "bcm-nand",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
|
.resource = nand_resource,
|
||||||
|
.num_resources = ARRAY_SIZE(nand_resource),
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device *devices[] __initdata = {
|
static struct platform_device *devices[] __initdata = {
|
||||||
|
|
66
arch/arm/mach-bcmring/include/mach/reg_nand.h
Normal file
66
arch/arm/mach-bcmring/include/mach/reg_nand.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright 2001 - 2008 Broadcom Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Unless you and Broadcom execute a separate written software license
|
||||||
|
* agreement governing use of this software, this software is licensed to you
|
||||||
|
* under the terms of the GNU General Public License version 2, available at
|
||||||
|
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||||
|
*
|
||||||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||||||
|
* software in any way with any other Broadcom software provided under a
|
||||||
|
* license other than the GPL, without Broadcom's express prior written
|
||||||
|
* consent.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
*
|
||||||
|
* REG_NAND.h
|
||||||
|
*
|
||||||
|
* PURPOSE:
|
||||||
|
*
|
||||||
|
* This file contains definitions for the nand registers:
|
||||||
|
*
|
||||||
|
* NOTES:
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(__ASM_ARCH_REG_NAND_H)
|
||||||
|
#define __ASM_ARCH_REG_NAND_H
|
||||||
|
|
||||||
|
/* ---- Include Files ---------------------------------------------------- */
|
||||||
|
#include <csp/reg.h>
|
||||||
|
#include <mach/reg_umi.h>
|
||||||
|
|
||||||
|
/* ---- Constants and Types ---------------------------------------------- */
|
||||||
|
|
||||||
|
#define HW_NAND_BASE MM_IO_BASE_NAND /* NAND Flash */
|
||||||
|
|
||||||
|
/* DMA accesses by the bootstrap need hard nonvirtual addresses */
|
||||||
|
#define REG_NAND_CMD __REG16(HW_NAND_BASE + 0)
|
||||||
|
#define REG_NAND_ADDR __REG16(HW_NAND_BASE + 4)
|
||||||
|
|
||||||
|
#define REG_NAND_PHYS_DATA16 (HW_NAND_BASE + 8)
|
||||||
|
#define REG_NAND_PHYS_DATA8 (HW_NAND_BASE + 8)
|
||||||
|
#define REG_NAND_DATA16 __REG16(REG_NAND_PHYS_DATA16)
|
||||||
|
#define REG_NAND_DATA8 __REG8(REG_NAND_PHYS_DATA8)
|
||||||
|
|
||||||
|
/* use appropriate offset to make sure it start at the 1K boundary */
|
||||||
|
#define REG_NAND_PHYS_DATA_DMA (HW_NAND_BASE + 0x400)
|
||||||
|
#define REG_NAND_DATA_DMA __REG32(REG_NAND_PHYS_DATA_DMA)
|
||||||
|
|
||||||
|
/* Linux DMA requires physical address of the data register */
|
||||||
|
#define REG_NAND_DATA16_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA16)
|
||||||
|
#define REG_NAND_DATA8_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA8)
|
||||||
|
#define REG_NAND_DATA_PADDR HW_IO_VIRT_TO_PHYS(REG_NAND_PHYS_DATA_DMA)
|
||||||
|
|
||||||
|
#define NAND_BUS_16BIT() (0)
|
||||||
|
#define NAND_BUS_8BIT() (!NAND_BUS_16BIT())
|
||||||
|
|
||||||
|
/* Register offsets */
|
||||||
|
#define REG_NAND_CMD_OFFSET (0)
|
||||||
|
#define REG_NAND_ADDR_OFFSET (4)
|
||||||
|
#define REG_NAND_DATA8_OFFSET (8)
|
||||||
|
|
||||||
|
#endif
|
237
arch/arm/mach-bcmring/include/mach/reg_umi.h
Normal file
237
arch/arm/mach-bcmring/include/mach/reg_umi.h
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright 2005 - 2008 Broadcom Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Unless you and Broadcom execute a separate written software license
|
||||||
|
* agreement governing use of this software, this software is licensed to you
|
||||||
|
* under the terms of the GNU General Public License version 2, available at
|
||||||
|
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||||
|
*
|
||||||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||||||
|
* software in any way with any other Broadcom software provided under a
|
||||||
|
* license other than the GPL, without Broadcom's express prior written
|
||||||
|
* consent.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
*
|
||||||
|
* REG_UMI.h
|
||||||
|
*
|
||||||
|
* PURPOSE:
|
||||||
|
*
|
||||||
|
* This file contains definitions for the nand registers:
|
||||||
|
*
|
||||||
|
* NOTES:
|
||||||
|
*
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#if !defined(__ASM_ARCH_REG_UMI_H)
|
||||||
|
#define __ASM_ARCH_REG_UMI_H
|
||||||
|
|
||||||
|
/* ---- Include Files ---------------------------------------------------- */
|
||||||
|
#include <csp/reg.h>
|
||||||
|
#include <mach/csp/mm_io.h>
|
||||||
|
|
||||||
|
/* ---- Constants and Types ---------------------------------------------- */
|
||||||
|
|
||||||
|
/* Unified Memory Interface Ctrl Register */
|
||||||
|
#define HW_UMI_BASE MM_IO_BASE_UMI
|
||||||
|
|
||||||
|
/* Flash bank 0 timing and control register */
|
||||||
|
#define REG_UMI_FLASH0_TCR __REG32(HW_UMI_BASE + 0x00)
|
||||||
|
/* Flash bank 1 timing and control register */
|
||||||
|
#define REG_UMI_FLASH1_TCR __REG32(HW_UMI_BASE + 0x04)
|
||||||
|
/* Flash bank 2 timing and control register */
|
||||||
|
#define REG_UMI_FLASH2_TCR __REG32(HW_UMI_BASE + 0x08)
|
||||||
|
/* MMD interface and control register */
|
||||||
|
#define REG_UMI_MMD_ICR __REG32(HW_UMI_BASE + 0x0c)
|
||||||
|
/* NAND timing and control register */
|
||||||
|
#define REG_UMI_NAND_TCR __REG32(HW_UMI_BASE + 0x18)
|
||||||
|
/* NAND ready/chip select register */
|
||||||
|
#define REG_UMI_NAND_RCSR __REG32(HW_UMI_BASE + 0x1c)
|
||||||
|
/* NAND ECC control & status register */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR __REG32(HW_UMI_BASE + 0x20)
|
||||||
|
/* NAND ECC data register XXB2B1B0 */
|
||||||
|
#define REG_UMI_NAND_ECC_DATA __REG32(HW_UMI_BASE + 0x24)
|
||||||
|
/* BCH ECC Parameter N */
|
||||||
|
#define REG_UMI_BCH_N __REG32(HW_UMI_BASE + 0x40)
|
||||||
|
/* BCH ECC Parameter T */
|
||||||
|
#define REG_UMI_BCH_K __REG32(HW_UMI_BASE + 0x44)
|
||||||
|
/* BCH ECC Parameter K */
|
||||||
|
#define REG_UMI_BCH_T __REG32(HW_UMI_BASE + 0x48)
|
||||||
|
/* BCH ECC Contro Status */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS __REG32(HW_UMI_BASE + 0x4C)
|
||||||
|
/* BCH WR ECC 31:0 */
|
||||||
|
#define REG_UMI_BCH_WR_ECC_0 __REG32(HW_UMI_BASE + 0x50)
|
||||||
|
/* BCH WR ECC 63:32 */
|
||||||
|
#define REG_UMI_BCH_WR_ECC_1 __REG32(HW_UMI_BASE + 0x54)
|
||||||
|
/* BCH WR ECC 95:64 */
|
||||||
|
#define REG_UMI_BCH_WR_ECC_2 __REG32(HW_UMI_BASE + 0x58)
|
||||||
|
/* BCH WR ECC 127:96 */
|
||||||
|
#define REG_UMI_BCH_WR_ECC_3 __REG32(HW_UMI_BASE + 0x5c)
|
||||||
|
/* BCH WR ECC 155:128 */
|
||||||
|
#define REG_UMI_BCH_WR_ECC_4 __REG32(HW_UMI_BASE + 0x60)
|
||||||
|
/* BCH Read Error Location 1,0 */
|
||||||
|
#define REG_UMI_BCH_RD_ERR_LOC_1_0 __REG32(HW_UMI_BASE + 0x64)
|
||||||
|
/* BCH Read Error Location 3,2 */
|
||||||
|
#define REG_UMI_BCH_RD_ERR_LOC_3_2 __REG32(HW_UMI_BASE + 0x68)
|
||||||
|
/* BCH Read Error Location 5,4 */
|
||||||
|
#define REG_UMI_BCH_RD_ERR_LOC_5_4 __REG32(HW_UMI_BASE + 0x6c)
|
||||||
|
/* BCH Read Error Location 7,6 */
|
||||||
|
#define REG_UMI_BCH_RD_ERR_LOC_7_6 __REG32(HW_UMI_BASE + 0x70)
|
||||||
|
/* BCH Read Error Location 9,8 */
|
||||||
|
#define REG_UMI_BCH_RD_ERR_LOC_9_8 __REG32(HW_UMI_BASE + 0x74)
|
||||||
|
/* BCH Read Error Location 11,10 */
|
||||||
|
#define REG_UMI_BCH_RD_ERR_LOC_B_A __REG32(HW_UMI_BASE + 0x78)
|
||||||
|
|
||||||
|
/* REG_UMI_FLASH0/1/2_TCR, REG_UMI_SRAM0/1_TCR bits */
|
||||||
|
/* Enable wait pin during burst write or read */
|
||||||
|
#define REG_UMI_TCR_WAITEN 0x80000000
|
||||||
|
/* Enable mem ctrlr to work iwth ext mem of lower freq than AHB clk */
|
||||||
|
#define REG_UMI_TCR_LOWFREQ 0x40000000
|
||||||
|
/* 1=synch write, 0=async write */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_SYNCWRITE 0x20000000
|
||||||
|
/* 1=synch read, 0=async read */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_SYNCREAD 0x10000000
|
||||||
|
/* 1=page mode read, 0=normal mode read */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PAGEREAD 0x08000000
|
||||||
|
/* page size/burst size (wrap only) */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_MASK 0x07000000
|
||||||
|
/* 4 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_4 0x00000000
|
||||||
|
/* 8 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_8 0x01000000
|
||||||
|
/* 16 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_16 0x02000000
|
||||||
|
/* 32 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_32 0x03000000
|
||||||
|
/* 64 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_64 0x04000000
|
||||||
|
/* 128 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_128 0x05000000
|
||||||
|
/* 256 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_256 0x06000000
|
||||||
|
/* 512 word */
|
||||||
|
#define REG_UMI_TCR_MEMTYPE_PGSZ_512 0x07000000
|
||||||
|
/* Page read access cycle / Burst write latency (n+2 / n+1) */
|
||||||
|
#define REG_UMI_TCR_TPRC_TWLC_MASK 0x00f80000
|
||||||
|
/* Bus turnaround cycle (n) */
|
||||||
|
#define REG_UMI_TCR_TBTA_MASK 0x00070000
|
||||||
|
/* Write pulse width cycle (n+1) */
|
||||||
|
#define REG_UMI_TCR_TWP_MASK 0x0000f800
|
||||||
|
/* Write recovery cycle (n+1) */
|
||||||
|
#define REG_UMI_TCR_TWR_MASK 0x00000600
|
||||||
|
/* Write address setup cycle (n+1) */
|
||||||
|
#define REG_UMI_TCR_TAS_MASK 0x00000180
|
||||||
|
/* Output enable delay cycle (n) */
|
||||||
|
#define REG_UMI_TCR_TOE_MASK 0x00000060
|
||||||
|
/* Read access cycle / Burst read latency (n+2 / n+1) */
|
||||||
|
#define REG_UMI_TCR_TRC_TLC_MASK 0x0000001f
|
||||||
|
|
||||||
|
/* REG_UMI_MMD_ICR bits */
|
||||||
|
/* Flash write protection pin control */
|
||||||
|
#define REG_UMI_MMD_ICR_FLASH_WP 0x8000
|
||||||
|
/* Extend hold time for sram0, sram1 csn (39 MHz operation) */
|
||||||
|
#define REG_UMI_MMD_ICR_XHCS 0x4000
|
||||||
|
/* Enable SDRAM 2 interface control */
|
||||||
|
#define REG_UMI_MMD_ICR_SDRAM2EN 0x2000
|
||||||
|
/* Enable merge of flash banks 0/1 to 512 MBit bank */
|
||||||
|
#define REG_UMI_MMD_ICR_INST512 0x1000
|
||||||
|
/* Enable merge of flash banks 1/2 to 512 MBit bank */
|
||||||
|
#define REG_UMI_MMD_ICR_DATA512 0x0800
|
||||||
|
/* Enable SDRAM interface control */
|
||||||
|
#define REG_UMI_MMD_ICR_SDRAMEN 0x0400
|
||||||
|
/* Polarity of busy state of Burst Wait Signal */
|
||||||
|
#define REG_UMI_MMD_ICR_WAITPOL 0x0200
|
||||||
|
/* Enable burst clock stopped when not accessing external burst flash/sram */
|
||||||
|
#define REG_UMI_MMD_ICR_BCLKSTOP 0x0100
|
||||||
|
/* Enable the peri1_csn to replace flash1_csn in 512 Mb flash mode */
|
||||||
|
#define REG_UMI_MMD_ICR_PERI1EN 0x0080
|
||||||
|
/* Enable the peri2_csn to replace sdram_csn */
|
||||||
|
#define REG_UMI_MMD_ICR_PERI2EN 0x0040
|
||||||
|
/* Enable the peri3_csn to replace sdram2_csn */
|
||||||
|
#define REG_UMI_MMD_ICR_PERI3EN 0x0020
|
||||||
|
/* Enable sram bank1 for H/W controlled MRS */
|
||||||
|
#define REG_UMI_MMD_ICR_MRSB1 0x0010
|
||||||
|
/* Enable sram bank0 for H/W controlled MRS */
|
||||||
|
#define REG_UMI_MMD_ICR_MRSB0 0x0008
|
||||||
|
/* Polarity for assert3ed state of H/W controlled MRS */
|
||||||
|
#define REG_UMI_MMD_ICR_MRSPOL 0x0004
|
||||||
|
/* 0: S/W controllable ZZ/MRS/CRE/P-Mode pin */
|
||||||
|
/* 1: H/W controlled ZZ/MRS/CRE/P-Mode, same timing as CS */
|
||||||
|
#define REG_UMI_MMD_ICR_MRSMODE 0x0002
|
||||||
|
/* MRS state for S/W controlled mode */
|
||||||
|
#define REG_UMI_MMD_ICR_MRSSTATE 0x0001
|
||||||
|
|
||||||
|
/* REG_UMI_NAND_TCR bits */
|
||||||
|
/* Enable software to control CS */
|
||||||
|
#define REG_UMI_NAND_TCR_CS_SWCTRL 0x80000000
|
||||||
|
/* 16-bit nand wordsize if set */
|
||||||
|
#define REG_UMI_NAND_TCR_WORD16 0x40000000
|
||||||
|
/* Bus turnaround cycle (n) */
|
||||||
|
#define REG_UMI_NAND_TCR_TBTA_MASK 0x00070000
|
||||||
|
/* Write pulse width cycle (n+1) */
|
||||||
|
#define REG_UMI_NAND_TCR_TWP_MASK 0x0000f800
|
||||||
|
/* Write recovery cycle (n+1) */
|
||||||
|
#define REG_UMI_NAND_TCR_TWR_MASK 0x00000600
|
||||||
|
/* Write address setup cycle (n+1) */
|
||||||
|
#define REG_UMI_NAND_TCR_TAS_MASK 0x00000180
|
||||||
|
/* Output enable delay cycle (n) */
|
||||||
|
#define REG_UMI_NAND_TCR_TOE_MASK 0x00000060
|
||||||
|
/* Read access cycle (n+2) */
|
||||||
|
#define REG_UMI_NAND_TCR_TRC_TLC_MASK 0x0000001f
|
||||||
|
|
||||||
|
/* REG_UMI_NAND_RCSR bits */
|
||||||
|
/* Status: Ready=1, Busy=0 */
|
||||||
|
#define REG_UMI_NAND_RCSR_RDY 0x02
|
||||||
|
/* Keep CS asserted during operation */
|
||||||
|
#define REG_UMI_NAND_RCSR_CS_ASSERTED 0x01
|
||||||
|
|
||||||
|
/* REG_UMI_NAND_ECC_CSR bits */
|
||||||
|
/* Interrupt status - read-only */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_NANDINT 0x80000000
|
||||||
|
/* Read: Status of ECC done, Write: clear ECC interrupt */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_ECCINT_RAW 0x00800000
|
||||||
|
/* Read: Status of R/B, Write: clear R/B interrupt */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_RBINT_RAW 0x00400000
|
||||||
|
/* 1 = Enable ECC Interrupt */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_ECCINT_ENABLE 0x00008000
|
||||||
|
/* 1 = Assert interrupt at rising edge of R/B_ */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_RBINT_ENABLE 0x00004000
|
||||||
|
/* Calculate ECC by 0=512 bytes, 1=256 bytes */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_256BYTE 0x00000080
|
||||||
|
/* Enable ECC in hardware */
|
||||||
|
#define REG_UMI_NAND_ECC_CSR_ECC_ENABLE 0x00000001
|
||||||
|
|
||||||
|
/* REG_UMI_BCH_CTRL_STATUS bits */
|
||||||
|
/* Shift to Indicate Number of correctable errors detected */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR_SHIFT 20
|
||||||
|
/* Indicate Number of correctable errors detected */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR 0x00F00000
|
||||||
|
/* Indicate Errors detected during read but uncorrectable */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR 0x00080000
|
||||||
|
/* Indicate Errors detected during read and are correctable */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_CORR_ERR 0x00040000
|
||||||
|
/* Flag indicates BCH's ECC status of read process are valid */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID 0x00020000
|
||||||
|
/* Flag indicates BCH's ECC status of write process are valid */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID 0x00010000
|
||||||
|
/* Pause ECC calculation */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC 0x00000010
|
||||||
|
/* Enable Interrupt */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_INT_EN 0x00000004
|
||||||
|
/* Enable ECC during read */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN 0x00000002
|
||||||
|
/* Enable ECC during write */
|
||||||
|
#define REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN 0x00000001
|
||||||
|
/* Mask for location */
|
||||||
|
#define REG_UMI_BCH_ERR_LOC_MASK 0x00001FFF
|
||||||
|
/* location within a byte */
|
||||||
|
#define REG_UMI_BCH_ERR_LOC_BYTE 0x00000007
|
||||||
|
/* location within a word */
|
||||||
|
#define REG_UMI_BCH_ERR_LOC_WORD 0x00000018
|
||||||
|
/* location within a page (512 byte) */
|
||||||
|
#define REG_UMI_BCH_ERR_LOC_PAGE 0x00001FE0
|
||||||
|
#define REG_UMI_BCH_ERR_LOC_ADDR(index) (__REG32(HW_UMI_BASE + 0x64 + (index / 2)*4) >> ((index % 2) * 16))
|
||||||
|
#endif
|
|
@ -79,6 +79,10 @@ struct davinci_nand_pdata { /* platform_data */
|
||||||
|
|
||||||
/* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
|
/* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
|
||||||
unsigned options;
|
unsigned options;
|
||||||
|
|
||||||
|
/* Main and mirror bbt descriptor overrides */
|
||||||
|
struct nand_bbt_descr *bbt_td;
|
||||||
|
struct nand_bbt_descr *bbt_md;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __ARCH_ARM_DAVINCI_NAND_H */
|
#endif /* __ARCH_ARM_DAVINCI_NAND_H */
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/nand.h>
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/mtd/onenand.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <asm/sizes.h>
|
#include <asm/sizes.h>
|
||||||
|
@ -149,7 +150,7 @@ static struct mtd_partition nhk8815_onenand_partitions[] = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct flash_platform_data nhk8815_onenand_data = {
|
static struct onenand_platform_data nhk8815_onenand_data = {
|
||||||
.parts = nhk8815_onenand_partitions,
|
.parts = nhk8815_onenand_partitions,
|
||||||
.nr_parts = ARRAY_SIZE(nhk8815_onenand_partitions),
|
.nr_parts = ARRAY_SIZE(nhk8815_onenand_partitions),
|
||||||
};
|
};
|
||||||
|
@ -163,7 +164,7 @@ static struct resource nhk8815_onenand_resource[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_device nhk8815_onenand_device = {
|
static struct platform_device nhk8815_onenand_device = {
|
||||||
.name = "onenand",
|
.name = "onenand-flash",
|
||||||
.id = -1,
|
.id = -1,
|
||||||
.dev = {
|
.dev = {
|
||||||
.platform_data = &nhk8815_onenand_data,
|
.platform_data = &nhk8815_onenand_data,
|
||||||
|
@ -174,10 +175,10 @@ static struct platform_device nhk8815_onenand_device = {
|
||||||
|
|
||||||
static void __init nhk8815_onenand_init(void)
|
static void __init nhk8815_onenand_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_ONENAND
|
#ifdef CONFIG_MTD_ONENAND
|
||||||
/* Set up SMCS0 for OneNand */
|
/* Set up SMCS0 for OneNand */
|
||||||
writel(0x000030db, FSMC_BCR0);
|
writel(0x000030db, FSMC_BCR(0));
|
||||||
writel(0x02100551, FSMC_BTR0);
|
writel(0x02100551, FSMC_BTR(0));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
struct mxc_nand_platform_data {
|
struct mxc_nand_platform_data {
|
||||||
int width; /* data bus width in bytes */
|
int width; /* data bus width in bytes */
|
||||||
int hw_ecc; /* 0 if supress hardware ECC */
|
int hw_ecc:1; /* 0 if supress hardware ECC */
|
||||||
|
int flash_bbt:1; /* set to 1 to use a flash based bbt */
|
||||||
};
|
};
|
||||||
#endif /* __ASM_ARCH_NAND_H */
|
#endif /* __ASM_ARCH_NAND_H */
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
* Setting this flag will allow the kernel to
|
* Setting this flag will allow the kernel to
|
||||||
* look for it at boot time and also skip the NAND
|
* look for it at boot time and also skip the NAND
|
||||||
* scan.
|
* scan.
|
||||||
|
* @options: Default value to set into 'struct nand_chip' options.
|
||||||
* @nr_chips: Number of chips in this set
|
* @nr_chips: Number of chips in this set
|
||||||
* @nr_partitions: Number of partitions pointed to by @partitions
|
* @nr_partitions: Number of partitions pointed to by @partitions
|
||||||
* @name: Name of set (optional)
|
* @name: Name of set (optional)
|
||||||
|
@ -31,6 +32,7 @@ struct s3c2410_nand_set {
|
||||||
unsigned int disable_ecc:1;
|
unsigned int disable_ecc:1;
|
||||||
unsigned int flash_bbt:1;
|
unsigned int flash_bbt:1;
|
||||||
|
|
||||||
|
unsigned int options;
|
||||||
int nr_chips;
|
int nr_chips;
|
||||||
int nr_partitions;
|
int nr_partitions;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
|
@ -43,15 +43,17 @@
|
||||||
// debugging, turns off buffer write mode if set to 1
|
// debugging, turns off buffer write mode if set to 1
|
||||||
#define FORCE_WORD_WRITE 0
|
#define FORCE_WORD_WRITE 0
|
||||||
|
|
||||||
#define MANUFACTURER_INTEL 0x0089
|
/* Intel chips */
|
||||||
#define I82802AB 0x00ad
|
#define I82802AB 0x00ad
|
||||||
#define I82802AC 0x00ac
|
#define I82802AC 0x00ac
|
||||||
#define PF38F4476 0x881c
|
#define PF38F4476 0x881c
|
||||||
#define MANUFACTURER_ST 0x0020
|
/* STMicroelectronics chips */
|
||||||
#define M50LPW080 0x002F
|
#define M50LPW080 0x002F
|
||||||
#define M50FLW080A 0x0080
|
#define M50FLW080A 0x0080
|
||||||
#define M50FLW080B 0x0081
|
#define M50FLW080B 0x0081
|
||||||
|
/* Atmel chips */
|
||||||
#define AT49BV640D 0x02de
|
#define AT49BV640D 0x02de
|
||||||
|
#define AT49BV640DT 0x02db
|
||||||
|
|
||||||
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||||
static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||||
|
@ -199,6 +201,16 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param)
|
||||||
cfi->cfiq->BufWriteTimeoutMax = 0;
|
cfi->cfiq->BufWriteTimeoutMax = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fixup_at49bv640dx_lock(struct mtd_info *mtd, void *param)
|
||||||
|
{
|
||||||
|
struct map_info *map = mtd->priv;
|
||||||
|
struct cfi_private *cfi = map->fldrv_priv;
|
||||||
|
struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
|
||||||
|
|
||||||
|
cfip->FeatureSupport |= (1 << 5);
|
||||||
|
mtd->flags |= MTD_POWERUP_LOCK;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
|
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
|
||||||
/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
|
/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
|
||||||
static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
|
static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
|
||||||
|
@ -283,6 +295,8 @@ static void fixup_unlock_powerup_lock(struct mtd_info *mtd, void *param)
|
||||||
|
|
||||||
static struct cfi_fixup cfi_fixup_table[] = {
|
static struct cfi_fixup cfi_fixup_table[] = {
|
||||||
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
|
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
|
||||||
|
{ CFI_MFR_ATMEL, AT49BV640D, fixup_at49bv640dx_lock, NULL },
|
||||||
|
{ CFI_MFR_ATMEL, AT49BV640DT, fixup_at49bv640dx_lock, NULL },
|
||||||
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
|
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
|
||||||
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
|
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
|
||||||
#endif
|
#endif
|
||||||
|
@ -294,16 +308,16 @@ static struct cfi_fixup cfi_fixup_table[] = {
|
||||||
#endif
|
#endif
|
||||||
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
|
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
|
||||||
{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
|
{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
|
||||||
{ MANUFACTURER_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, },
|
{ CFI_MFR_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, },
|
||||||
{ 0, 0, NULL, NULL }
|
{ 0, 0, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct cfi_fixup jedec_fixup_table[] = {
|
static struct cfi_fixup jedec_fixup_table[] = {
|
||||||
{ MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, },
|
{ CFI_MFR_INTEL, I82802AB, fixup_use_fwh_lock, NULL, },
|
||||||
{ MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, },
|
{ CFI_MFR_INTEL, I82802AC, fixup_use_fwh_lock, NULL, },
|
||||||
{ MANUFACTURER_ST, M50LPW080, fixup_use_fwh_lock, NULL, },
|
{ CFI_MFR_ST, M50LPW080, fixup_use_fwh_lock, NULL, },
|
||||||
{ MANUFACTURER_ST, M50FLW080A, fixup_use_fwh_lock, NULL, },
|
{ CFI_MFR_ST, M50FLW080A, fixup_use_fwh_lock, NULL, },
|
||||||
{ MANUFACTURER_ST, M50FLW080B, fixup_use_fwh_lock, NULL, },
|
{ CFI_MFR_ST, M50FLW080B, fixup_use_fwh_lock, NULL, },
|
||||||
{ 0, 0, NULL, NULL }
|
{ 0, 0, NULL, NULL }
|
||||||
};
|
};
|
||||||
static struct cfi_fixup fixup_table[] = {
|
static struct cfi_fixup fixup_table[] = {
|
||||||
|
@ -319,7 +333,7 @@ static struct cfi_fixup fixup_table[] = {
|
||||||
static void cfi_fixup_major_minor(struct cfi_private *cfi,
|
static void cfi_fixup_major_minor(struct cfi_private *cfi,
|
||||||
struct cfi_pri_intelext *extp)
|
struct cfi_pri_intelext *extp)
|
||||||
{
|
{
|
||||||
if (cfi->mfr == MANUFACTURER_INTEL &&
|
if (cfi->mfr == CFI_MFR_INTEL &&
|
||||||
cfi->id == PF38F4476 && extp->MinorVersion == '3')
|
cfi->id == PF38F4476 && extp->MinorVersion == '3')
|
||||||
extp->MinorVersion = '1';
|
extp->MinorVersion = '1';
|
||||||
}
|
}
|
||||||
|
@ -2235,7 +2249,7 @@ static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
|
|
||||||
/* Some chips have OTP located in the _top_ partition only.
|
/* Some chips have OTP located in the _top_ partition only.
|
||||||
For example: Intel 28F256L18T (T means top-parameter device) */
|
For example: Intel 28F256L18T (T means top-parameter device) */
|
||||||
if (cfi->mfr == MANUFACTURER_INTEL) {
|
if (cfi->mfr == CFI_MFR_INTEL) {
|
||||||
switch (cfi->id) {
|
switch (cfi->id) {
|
||||||
case 0x880b:
|
case 0x880b:
|
||||||
case 0x880c:
|
case 0x880c:
|
||||||
|
@ -2564,6 +2578,7 @@ static int cfi_intelext_reset(struct mtd_info *mtd)
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
map_write(map, CMD(0xff), chip->start);
|
map_write(map, CMD(0xff), chip->start);
|
||||||
chip->state = FL_SHUTDOWN;
|
chip->state = FL_SHUTDOWN;
|
||||||
|
put_chip(map, chip, chip->start);
|
||||||
}
|
}
|
||||||
spin_unlock(chip->mutex);
|
spin_unlock(chip->mutex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -490,10 +490,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* FIXME: erase-suspend-program is broken. See
|
|
||||||
http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */
|
|
||||||
printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");
|
|
||||||
|
|
||||||
__module_get(THIS_MODULE);
|
__module_get(THIS_MODULE);
|
||||||
return mtd;
|
return mtd;
|
||||||
|
|
||||||
|
@ -573,7 +569,6 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
||||||
|
|
||||||
if (time_after(jiffies, timeo)) {
|
if (time_after(jiffies, timeo)) {
|
||||||
printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
|
printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
|
||||||
spin_unlock(chip->mutex);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
spin_unlock(chip->mutex);
|
spin_unlock(chip->mutex);
|
||||||
|
@ -589,15 +584,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case FL_ERASING:
|
case FL_ERASING:
|
||||||
if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
|
if (!cfip || !(cfip->EraseSuspend & (0x1|0x2)) ||
|
||||||
goto sleep;
|
!(mode == FL_READY || mode == FL_POINT ||
|
||||||
|
(mode == FL_WRITING && (cfip->EraseSuspend & 0x2))))
|
||||||
if (!( mode == FL_READY
|
|
||||||
|| mode == FL_POINT
|
|
||||||
|| !cfip
|
|
||||||
|| (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
|
|
||||||
|| (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)
|
|
||||||
)))
|
|
||||||
goto sleep;
|
goto sleep;
|
||||||
|
|
||||||
/* We could check to see if we're trying to access the sector
|
/* We could check to see if we're trying to access the sector
|
||||||
|
|
|
@ -69,6 +69,13 @@ int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
|
||||||
/* ST M29DW chips */
|
/* ST M29DW chips */
|
||||||
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
|
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
|
||||||
cfi_send_gen_cmd(0x98, 0x555, 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;
|
||||||
|
/* some old SST chips, e.g. 39VF160x/39VF320x */
|
||||||
|
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
|
||||||
|
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))
|
if (cfi_qry_present(map, base, cfi))
|
||||||
return 1;
|
return 1;
|
||||||
/* QRY not found */
|
/* QRY not found */
|
||||||
|
|
|
@ -142,8 +142,8 @@
|
||||||
|
|
||||||
/* ST - www.st.com */
|
/* ST - www.st.com */
|
||||||
#define M29F800AB 0x0058
|
#define M29F800AB 0x0058
|
||||||
#define M29W800DT 0x00D7
|
#define M29W800DT 0x22D7
|
||||||
#define M29W800DB 0x005B
|
#define M29W800DB 0x225B
|
||||||
#define M29W400DT 0x00EE
|
#define M29W400DT 0x00EE
|
||||||
#define M29W400DB 0x00EF
|
#define M29W400DB 0x00EF
|
||||||
#define M29W160DT 0x22C4
|
#define M29W160DT 0x22C4
|
||||||
|
@ -1575,7 +1575,7 @@ static const struct amd_flash_info jedec_table[] = {
|
||||||
.dev_id = M29W800DT,
|
.dev_id = M29W800DT,
|
||||||
.name = "ST M29W800DT",
|
.name = "ST M29W800DT",
|
||||||
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
|
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
|
||||||
.uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */
|
.uaddr = MTD_UADDR_0x0AAA_0x0555,
|
||||||
.dev_size = SIZE_1MiB,
|
.dev_size = SIZE_1MiB,
|
||||||
.cmd_set = P_ID_AMD_STD,
|
.cmd_set = P_ID_AMD_STD,
|
||||||
.nr_regions = 4,
|
.nr_regions = 4,
|
||||||
|
@ -1590,7 +1590,7 @@ static const struct amd_flash_info jedec_table[] = {
|
||||||
.dev_id = M29W800DB,
|
.dev_id = M29W800DB,
|
||||||
.name = "ST M29W800DB",
|
.name = "ST M29W800DB",
|
||||||
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
|
.devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
|
||||||
.uaddr = MTD_UADDR_0x5555_0x2AAA, /* ???? */
|
.uaddr = MTD_UADDR_0x0AAA_0x0555,
|
||||||
.dev_size = SIZE_1MiB,
|
.dev_size = SIZE_1MiB,
|
||||||
.cmd_set = P_ID_AMD_STD,
|
.cmd_set = P_ID_AMD_STD,
|
||||||
.nr_regions = 4,
|
.nr_regions = 4,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
#include <linux/mtd/partitions.h>
|
#include <linux/mtd/partitions.h>
|
||||||
|
@ -29,9 +30,6 @@
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/flash.h>
|
#include <linux/spi/flash.h>
|
||||||
|
|
||||||
|
|
||||||
#define FLASH_PAGESIZE 256
|
|
||||||
|
|
||||||
/* Flash opcodes. */
|
/* Flash opcodes. */
|
||||||
#define OPCODE_WREN 0x06 /* Write enable */
|
#define OPCODE_WREN 0x06 /* Write enable */
|
||||||
#define OPCODE_RDSR 0x05 /* Read status register */
|
#define OPCODE_RDSR 0x05 /* Read status register */
|
||||||
|
@ -61,7 +59,7 @@
|
||||||
|
|
||||||
/* Define max times to check status register before we give up. */
|
/* Define max times to check status register before we give up. */
|
||||||
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
|
#define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */
|
||||||
#define CMD_SIZE 4
|
#define MAX_CMD_SIZE 4
|
||||||
|
|
||||||
#ifdef CONFIG_M25PXX_USE_FAST_READ
|
#ifdef CONFIG_M25PXX_USE_FAST_READ
|
||||||
#define OPCODE_READ OPCODE_FAST_READ
|
#define OPCODE_READ OPCODE_FAST_READ
|
||||||
|
@ -78,8 +76,10 @@ struct m25p {
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
struct mtd_info mtd;
|
struct mtd_info mtd;
|
||||||
unsigned partitioned:1;
|
unsigned partitioned:1;
|
||||||
|
u16 page_size;
|
||||||
|
u16 addr_width;
|
||||||
u8 erase_opcode;
|
u8 erase_opcode;
|
||||||
u8 command[CMD_SIZE + FAST_READ_DUMMY_BYTE];
|
u8 *command;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
|
static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd)
|
||||||
|
@ -198,6 +198,19 @@ static int erase_chip(struct m25p *flash)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void m25p_addr2cmd(struct m25p *flash, unsigned int addr, u8 *cmd)
|
||||||
|
{
|
||||||
|
/* opcode is in cmd[0] */
|
||||||
|
cmd[1] = addr >> (flash->addr_width * 8 - 8);
|
||||||
|
cmd[2] = addr >> (flash->addr_width * 8 - 16);
|
||||||
|
cmd[3] = addr >> (flash->addr_width * 8 - 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int m25p_cmdsz(struct m25p *flash)
|
||||||
|
{
|
||||||
|
return 1 + flash->addr_width;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Erase one sector of flash memory at offset ``offset'' which is any
|
* Erase one sector of flash memory at offset ``offset'' which is any
|
||||||
* address within the sector which should be erased.
|
* address within the sector which should be erased.
|
||||||
|
@ -219,11 +232,9 @@ static int erase_sector(struct m25p *flash, u32 offset)
|
||||||
|
|
||||||
/* Set up command buffer. */
|
/* Set up command buffer. */
|
||||||
flash->command[0] = flash->erase_opcode;
|
flash->command[0] = flash->erase_opcode;
|
||||||
flash->command[1] = offset >> 16;
|
m25p_addr2cmd(flash, offset, flash->command);
|
||||||
flash->command[2] = offset >> 8;
|
|
||||||
flash->command[3] = offset;
|
|
||||||
|
|
||||||
spi_write(flash->spi, flash->command, CMD_SIZE);
|
spi_write(flash->spi, flash->command, m25p_cmdsz(flash));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -325,7 +336,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
* Should add 1 byte DUMMY_BYTE.
|
* Should add 1 byte DUMMY_BYTE.
|
||||||
*/
|
*/
|
||||||
t[0].tx_buf = flash->command;
|
t[0].tx_buf = flash->command;
|
||||||
t[0].len = CMD_SIZE + FAST_READ_DUMMY_BYTE;
|
t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
|
||||||
spi_message_add_tail(&t[0], &m);
|
spi_message_add_tail(&t[0], &m);
|
||||||
|
|
||||||
t[1].rx_buf = buf;
|
t[1].rx_buf = buf;
|
||||||
|
@ -352,13 +363,11 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
|
|
||||||
/* Set up the write data buffer. */
|
/* Set up the write data buffer. */
|
||||||
flash->command[0] = OPCODE_READ;
|
flash->command[0] = OPCODE_READ;
|
||||||
flash->command[1] = from >> 16;
|
m25p_addr2cmd(flash, from, flash->command);
|
||||||
flash->command[2] = from >> 8;
|
|
||||||
flash->command[3] = from;
|
|
||||||
|
|
||||||
spi_sync(flash->spi, &m);
|
spi_sync(flash->spi, &m);
|
||||||
|
|
||||||
*retlen = m.actual_length - CMD_SIZE - FAST_READ_DUMMY_BYTE;
|
*retlen = m.actual_length - m25p_cmdsz(flash) - FAST_READ_DUMMY_BYTE;
|
||||||
|
|
||||||
mutex_unlock(&flash->lock);
|
mutex_unlock(&flash->lock);
|
||||||
|
|
||||||
|
@ -396,7 +405,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
memset(t, 0, (sizeof t));
|
memset(t, 0, (sizeof t));
|
||||||
|
|
||||||
t[0].tx_buf = flash->command;
|
t[0].tx_buf = flash->command;
|
||||||
t[0].len = CMD_SIZE;
|
t[0].len = m25p_cmdsz(flash);
|
||||||
spi_message_add_tail(&t[0], &m);
|
spi_message_add_tail(&t[0], &m);
|
||||||
|
|
||||||
t[1].tx_buf = buf;
|
t[1].tx_buf = buf;
|
||||||
|
@ -414,41 +423,36 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
|
|
||||||
/* Set up the opcode in the write buffer. */
|
/* Set up the opcode in the write buffer. */
|
||||||
flash->command[0] = OPCODE_PP;
|
flash->command[0] = OPCODE_PP;
|
||||||
flash->command[1] = to >> 16;
|
m25p_addr2cmd(flash, to, flash->command);
|
||||||
flash->command[2] = to >> 8;
|
|
||||||
flash->command[3] = to;
|
|
||||||
|
|
||||||
/* what page do we start with? */
|
page_offset = to & (flash->page_size - 1);
|
||||||
page_offset = to % FLASH_PAGESIZE;
|
|
||||||
|
|
||||||
/* do all the bytes fit onto one page? */
|
/* do all the bytes fit onto one page? */
|
||||||
if (page_offset + len <= FLASH_PAGESIZE) {
|
if (page_offset + len <= flash->page_size) {
|
||||||
t[1].len = len;
|
t[1].len = len;
|
||||||
|
|
||||||
spi_sync(flash->spi, &m);
|
spi_sync(flash->spi, &m);
|
||||||
|
|
||||||
*retlen = m.actual_length - CMD_SIZE;
|
*retlen = m.actual_length - m25p_cmdsz(flash);
|
||||||
} else {
|
} else {
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
/* the size of data remaining on the first page */
|
/* the size of data remaining on the first page */
|
||||||
page_size = FLASH_PAGESIZE - page_offset;
|
page_size = flash->page_size - page_offset;
|
||||||
|
|
||||||
t[1].len = page_size;
|
t[1].len = page_size;
|
||||||
spi_sync(flash->spi, &m);
|
spi_sync(flash->spi, &m);
|
||||||
|
|
||||||
*retlen = m.actual_length - CMD_SIZE;
|
*retlen = m.actual_length - m25p_cmdsz(flash);
|
||||||
|
|
||||||
/* write everything in PAGESIZE chunks */
|
/* write everything in flash->page_size chunks */
|
||||||
for (i = page_size; i < len; i += page_size) {
|
for (i = page_size; i < len; i += page_size) {
|
||||||
page_size = len - i;
|
page_size = len - i;
|
||||||
if (page_size > FLASH_PAGESIZE)
|
if (page_size > flash->page_size)
|
||||||
page_size = FLASH_PAGESIZE;
|
page_size = flash->page_size;
|
||||||
|
|
||||||
/* write the next page to flash */
|
/* write the next page to flash */
|
||||||
flash->command[1] = (to + i) >> 16;
|
m25p_addr2cmd(flash, to + i, flash->command);
|
||||||
flash->command[2] = (to + i) >> 8;
|
|
||||||
flash->command[3] = (to + i);
|
|
||||||
|
|
||||||
t[1].tx_buf = buf + i;
|
t[1].tx_buf = buf + i;
|
||||||
t[1].len = page_size;
|
t[1].len = page_size;
|
||||||
|
@ -460,7 +464,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
spi_sync(flash->spi, &m);
|
spi_sync(flash->spi, &m);
|
||||||
|
|
||||||
if (retlen)
|
if (retlen)
|
||||||
*retlen += m.actual_length - CMD_SIZE;
|
*retlen += m.actual_length - m25p_cmdsz(flash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +496,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
memset(t, 0, (sizeof t));
|
memset(t, 0, (sizeof t));
|
||||||
|
|
||||||
t[0].tx_buf = flash->command;
|
t[0].tx_buf = flash->command;
|
||||||
t[0].len = CMD_SIZE;
|
t[0].len = m25p_cmdsz(flash);
|
||||||
spi_message_add_tail(&t[0], &m);
|
spi_message_add_tail(&t[0], &m);
|
||||||
|
|
||||||
t[1].tx_buf = buf;
|
t[1].tx_buf = buf;
|
||||||
|
@ -511,9 +515,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
/* Start write from odd address. */
|
/* Start write from odd address. */
|
||||||
if (actual) {
|
if (actual) {
|
||||||
flash->command[0] = OPCODE_BP;
|
flash->command[0] = OPCODE_BP;
|
||||||
flash->command[1] = to >> 16;
|
m25p_addr2cmd(flash, to, flash->command);
|
||||||
flash->command[2] = to >> 8;
|
|
||||||
flash->command[3] = to;
|
|
||||||
|
|
||||||
/* write one byte. */
|
/* write one byte. */
|
||||||
t[1].len = 1;
|
t[1].len = 1;
|
||||||
|
@ -521,17 +523,15 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
ret = wait_till_ready(flash);
|
ret = wait_till_ready(flash);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto time_out;
|
goto time_out;
|
||||||
*retlen += m.actual_length - CMD_SIZE;
|
*retlen += m.actual_length - m25p_cmdsz(flash);
|
||||||
}
|
}
|
||||||
to += actual;
|
to += actual;
|
||||||
|
|
||||||
flash->command[0] = OPCODE_AAI_WP;
|
flash->command[0] = OPCODE_AAI_WP;
|
||||||
flash->command[1] = to >> 16;
|
m25p_addr2cmd(flash, to, flash->command);
|
||||||
flash->command[2] = to >> 8;
|
|
||||||
flash->command[3] = to;
|
|
||||||
|
|
||||||
/* Write out most of the data here. */
|
/* Write out most of the data here. */
|
||||||
cmd_sz = CMD_SIZE;
|
cmd_sz = m25p_cmdsz(flash);
|
||||||
for (; actual < len - 1; actual += 2) {
|
for (; actual < len - 1; actual += 2) {
|
||||||
t[0].len = cmd_sz;
|
t[0].len = cmd_sz;
|
||||||
/* write two bytes. */
|
/* write two bytes. */
|
||||||
|
@ -555,10 +555,8 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
if (actual != len) {
|
if (actual != len) {
|
||||||
write_enable(flash);
|
write_enable(flash);
|
||||||
flash->command[0] = OPCODE_BP;
|
flash->command[0] = OPCODE_BP;
|
||||||
flash->command[1] = to >> 16;
|
m25p_addr2cmd(flash, to, flash->command);
|
||||||
flash->command[2] = to >> 8;
|
t[0].len = m25p_cmdsz(flash);
|
||||||
flash->command[3] = to;
|
|
||||||
t[0].len = CMD_SIZE;
|
|
||||||
t[1].len = 1;
|
t[1].len = 1;
|
||||||
t[1].tx_buf = buf + actual;
|
t[1].tx_buf = buf + actual;
|
||||||
|
|
||||||
|
@ -566,7 +564,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
ret = wait_till_ready(flash);
|
ret = wait_till_ready(flash);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto time_out;
|
goto time_out;
|
||||||
*retlen += m.actual_length - CMD_SIZE;
|
*retlen += m.actual_length - m25p_cmdsz(flash);
|
||||||
write_disable(flash);
|
write_disable(flash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,8 +580,6 @@ time_out:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct flash_info {
|
struct flash_info {
|
||||||
char *name;
|
|
||||||
|
|
||||||
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
|
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
|
||||||
* a high byte of zero plus three data bytes: the manufacturer id,
|
* a high byte of zero plus three data bytes: the manufacturer id,
|
||||||
* then a two byte device id.
|
* then a two byte device id.
|
||||||
|
@ -597,87 +593,119 @@ struct flash_info {
|
||||||
unsigned sector_size;
|
unsigned sector_size;
|
||||||
u16 n_sectors;
|
u16 n_sectors;
|
||||||
|
|
||||||
|
u16 page_size;
|
||||||
|
u16 addr_width;
|
||||||
|
|
||||||
u16 flags;
|
u16 flags;
|
||||||
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
||||||
|
#define M25P_NO_ERASE 0x02 /* No erase command needed */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||||
|
((kernel_ulong_t)&(struct flash_info) { \
|
||||||
|
.jedec_id = (_jedec_id), \
|
||||||
|
.ext_id = (_ext_id), \
|
||||||
|
.sector_size = (_sector_size), \
|
||||||
|
.n_sectors = (_n_sectors), \
|
||||||
|
.page_size = 256, \
|
||||||
|
.addr_width = 3, \
|
||||||
|
.flags = (_flags), \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \
|
||||||
|
((kernel_ulong_t)&(struct flash_info) { \
|
||||||
|
.sector_size = (_sector_size), \
|
||||||
|
.n_sectors = (_n_sectors), \
|
||||||
|
.page_size = (_page_size), \
|
||||||
|
.addr_width = (_addr_width), \
|
||||||
|
.flags = M25P_NO_ERASE, \
|
||||||
|
})
|
||||||
|
|
||||||
/* NOTE: double check command sets and memory organization when you add
|
/* NOTE: double check command sets and memory organization when you add
|
||||||
* more flash chips. This current list focusses on newer chips, which
|
* more flash chips. This current list focusses on newer chips, which
|
||||||
* have been converging on command sets which including JEDEC ID.
|
* have been converging on command sets which including JEDEC ID.
|
||||||
*/
|
*/
|
||||||
static struct flash_info __devinitdata m25p_data [] = {
|
static const struct spi_device_id m25p_ids[] = {
|
||||||
|
|
||||||
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
||||||
{ "at25fs010", 0x1f6601, 0, 32 * 1024, 4, SECT_4K, },
|
{ "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4, SECT_4K) },
|
||||||
{ "at25fs040", 0x1f6604, 0, 64 * 1024, 8, SECT_4K, },
|
{ "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
|
|
||||||
{ "at25df041a", 0x1f4401, 0, 64 * 1024, 8, SECT_4K, },
|
{ "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "at25df641", 0x1f4800, 0, 64 * 1024, 128, SECT_4K, },
|
{ "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
|
|
||||||
{ "at26f004", 0x1f0400, 0, 64 * 1024, 8, SECT_4K, },
|
{ "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "at26df081a", 0x1f4501, 0, 64 * 1024, 16, SECT_4K, },
|
{ "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, },
|
{ "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) },
|
||||||
{ "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
|
{ "at26df321", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
|
|
||||||
/* Macronix */
|
/* Macronix */
|
||||||
{ "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, },
|
{ "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, },
|
{ "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) },
|
||||||
{ "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
|
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) },
|
||||||
{ "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, },
|
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
|
||||||
|
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||||
|
|
||||||
/* Spansion -- single (large) sector size only, at least
|
/* Spansion -- single (large) sector size only, at least
|
||||||
* for the chips listed here (without boot sectors).
|
* for the chips listed here (without boot sectors).
|
||||||
*/
|
*/
|
||||||
{ "s25sl004a", 0x010212, 0, 64 * 1024, 8, },
|
{ "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
|
||||||
{ "s25sl008a", 0x010213, 0, 64 * 1024, 16, },
|
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
|
||||||
{ "s25sl016a", 0x010214, 0, 64 * 1024, 32, },
|
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
|
||||||
{ "s25sl032a", 0x010215, 0, 64 * 1024, 64, },
|
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
|
||||||
{ "s25sl064a", 0x010216, 0, 64 * 1024, 128, },
|
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
|
||||||
{ "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, },
|
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
|
||||||
{ "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, },
|
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
|
||||||
{ "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, },
|
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
|
||||||
{ "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, },
|
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
|
||||||
|
|
||||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||||
{ "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, },
|
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, },
|
{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, },
|
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) },
|
||||||
{ "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, },
|
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
{ "sst25wf512", 0xbf2501, 0, 64 * 1024, 1, SECT_4K, },
|
{ "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K) },
|
||||||
{ "sst25wf010", 0xbf2502, 0, 64 * 1024, 2, SECT_4K, },
|
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K) },
|
||||||
{ "sst25wf020", 0xbf2503, 0, 64 * 1024, 4, SECT_4K, },
|
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K) },
|
||||||
{ "sst25wf040", 0xbf2504, 0, 64 * 1024, 8, SECT_4K, },
|
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
|
|
||||||
/* ST Microelectronics -- newer production may have feature updates */
|
/* ST Microelectronics -- newer production may have feature updates */
|
||||||
{ "m25p05", 0x202010, 0, 32 * 1024, 2, },
|
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
|
||||||
{ "m25p10", 0x202011, 0, 32 * 1024, 4, },
|
{ "m25p10", INFO(0x202011, 0, 32 * 1024, 4, 0) },
|
||||||
{ "m25p20", 0x202012, 0, 64 * 1024, 4, },
|
{ "m25p20", INFO(0x202012, 0, 64 * 1024, 4, 0) },
|
||||||
{ "m25p40", 0x202013, 0, 64 * 1024, 8, },
|
{ "m25p40", INFO(0x202013, 0, 64 * 1024, 8, 0) },
|
||||||
{ "m25p80", 0, 0, 64 * 1024, 16, },
|
{ "m25p80", INFO(0x202014, 0, 64 * 1024, 16, 0) },
|
||||||
{ "m25p16", 0x202015, 0, 64 * 1024, 32, },
|
{ "m25p16", INFO(0x202015, 0, 64 * 1024, 32, 0) },
|
||||||
{ "m25p32", 0x202016, 0, 64 * 1024, 64, },
|
{ "m25p32", INFO(0x202016, 0, 64 * 1024, 64, 0) },
|
||||||
{ "m25p64", 0x202017, 0, 64 * 1024, 128, },
|
{ "m25p64", INFO(0x202017, 0, 64 * 1024, 128, 0) },
|
||||||
{ "m25p128", 0x202018, 0, 256 * 1024, 64, },
|
{ "m25p128", INFO(0x202018, 0, 256 * 1024, 64, 0) },
|
||||||
|
|
||||||
{ "m45pe10", 0x204011, 0, 64 * 1024, 2, },
|
{ "m45pe10", INFO(0x204011, 0, 64 * 1024, 2, 0) },
|
||||||
{ "m45pe80", 0x204014, 0, 64 * 1024, 16, },
|
{ "m45pe80", INFO(0x204014, 0, 64 * 1024, 16, 0) },
|
||||||
{ "m45pe16", 0x204015, 0, 64 * 1024, 32, },
|
{ "m45pe16", INFO(0x204015, 0, 64 * 1024, 32, 0) },
|
||||||
|
|
||||||
{ "m25pe80", 0x208014, 0, 64 * 1024, 16, },
|
{ "m25pe80", INFO(0x208014, 0, 64 * 1024, 16, 0) },
|
||||||
{ "m25pe16", 0x208015, 0, 64 * 1024, 32, SECT_4K, },
|
{ "m25pe16", INFO(0x208015, 0, 64 * 1024, 32, SECT_4K) },
|
||||||
|
|
||||||
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
|
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
|
||||||
{ "w25x10", 0xef3011, 0, 64 * 1024, 2, SECT_4K, },
|
{ "w25x10", INFO(0xef3011, 0, 64 * 1024, 2, SECT_4K) },
|
||||||
{ "w25x20", 0xef3012, 0, 64 * 1024, 4, SECT_4K, },
|
{ "w25x20", INFO(0xef3012, 0, 64 * 1024, 4, SECT_4K) },
|
||||||
{ "w25x40", 0xef3013, 0, 64 * 1024, 8, SECT_4K, },
|
{ "w25x40", INFO(0xef3013, 0, 64 * 1024, 8, SECT_4K) },
|
||||||
{ "w25x80", 0xef3014, 0, 64 * 1024, 16, SECT_4K, },
|
{ "w25x80", INFO(0xef3014, 0, 64 * 1024, 16, SECT_4K) },
|
||||||
{ "w25x16", 0xef3015, 0, 64 * 1024, 32, SECT_4K, },
|
{ "w25x16", INFO(0xef3015, 0, 64 * 1024, 32, SECT_4K) },
|
||||||
{ "w25x32", 0xef3016, 0, 64 * 1024, 64, SECT_4K, },
|
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
|
||||||
{ "w25x64", 0xef3017, 0, 64 * 1024, 128, SECT_4K, },
|
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||||
};
|
|
||||||
|
|
||||||
static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
|
/* Catalyst / On Semiconductor -- non-JEDEC */
|
||||||
|
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
|
||||||
|
{ "cat25c03", CAT25_INFO( 32, 8, 16, 2) },
|
||||||
|
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2) },
|
||||||
|
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2) },
|
||||||
|
{ "cat25128", CAT25_INFO(2048, 8, 64, 2) },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(spi, m25p_ids);
|
||||||
|
|
||||||
|
static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
int tmp;
|
int tmp;
|
||||||
u8 code = OPCODE_RDID;
|
u8 code = OPCODE_RDID;
|
||||||
|
@ -702,18 +730,24 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
|
||||||
jedec = jedec << 8;
|
jedec = jedec << 8;
|
||||||
jedec |= id[2];
|
jedec |= id[2];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some chips (like Numonyx M25P80) have JEDEC and non-JEDEC variants,
|
||||||
|
* which depend on technology process. Officially RDID command doesn't
|
||||||
|
* exist for non-JEDEC chips, but for compatibility they return ID 0.
|
||||||
|
*/
|
||||||
|
if (jedec == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
ext_jedec = id[3] << 8 | id[4];
|
ext_jedec = id[3] << 8 | id[4];
|
||||||
|
|
||||||
for (tmp = 0, info = m25p_data;
|
for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
|
||||||
tmp < ARRAY_SIZE(m25p_data);
|
info = (void *)m25p_ids[tmp].driver_data;
|
||||||
tmp++, info++) {
|
|
||||||
if (info->jedec_id == jedec) {
|
if (info->jedec_id == jedec) {
|
||||||
if (info->ext_id != 0 && info->ext_id != ext_jedec)
|
if (info->ext_id != 0 && info->ext_id != ext_jedec)
|
||||||
continue;
|
continue;
|
||||||
return info;
|
return &m25p_ids[tmp];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,6 +759,7 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
|
||||||
*/
|
*/
|
||||||
static int __devinit m25p_probe(struct spi_device *spi)
|
static int __devinit m25p_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
|
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||||
struct flash_platform_data *data;
|
struct flash_platform_data *data;
|
||||||
struct m25p *flash;
|
struct m25p *flash;
|
||||||
struct flash_info *info;
|
struct flash_info *info;
|
||||||
|
@ -737,50 +772,65 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||||
*/
|
*/
|
||||||
data = spi->dev.platform_data;
|
data = spi->dev.platform_data;
|
||||||
if (data && data->type) {
|
if (data && data->type) {
|
||||||
for (i = 0, info = m25p_data;
|
const struct spi_device_id *plat_id;
|
||||||
i < ARRAY_SIZE(m25p_data);
|
|
||||||
i++, info++) {
|
for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
|
||||||
if (strcmp(data->type, info->name) == 0)
|
plat_id = &m25p_ids[i];
|
||||||
break;
|
if (strcmp(data->type, plat_id->name))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unrecognized chip? */
|
if (plat_id)
|
||||||
if (i == ARRAY_SIZE(m25p_data)) {
|
id = plat_id;
|
||||||
DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
|
else
|
||||||
dev_name(&spi->dev), data->type);
|
dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
|
||||||
info = NULL;
|
}
|
||||||
|
|
||||||
/* recognized; is that chip really what's there? */
|
info = (void *)id->driver_data;
|
||||||
} else if (info->jedec_id) {
|
|
||||||
struct flash_info *chip = jedec_probe(spi);
|
|
||||||
|
|
||||||
if (!chip || chip != info) {
|
if (info->jedec_id) {
|
||||||
dev_warn(&spi->dev, "found %s, expected %s\n",
|
const struct spi_device_id *jid;
|
||||||
chip ? chip->name : "UNKNOWN",
|
|
||||||
info->name);
|
jid = jedec_probe(spi);
|
||||||
info = NULL;
|
if (!jid) {
|
||||||
}
|
dev_info(&spi->dev, "non-JEDEC variant of %s\n",
|
||||||
|
id->name);
|
||||||
|
} else if (jid != id) {
|
||||||
|
/*
|
||||||
|
* JEDEC knows better, so overwrite platform ID. We
|
||||||
|
* can't trust partitions any longer, but we'll let
|
||||||
|
* mtd apply them anyway, since some partitions may be
|
||||||
|
* marked read-only, and we don't want to lose that
|
||||||
|
* information, even if it's not 100% accurate.
|
||||||
|
*/
|
||||||
|
dev_warn(&spi->dev, "found %s, expected %s\n",
|
||||||
|
jid->name, id->name);
|
||||||
|
id = jid;
|
||||||
|
info = (void *)jid->driver_data;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
info = jedec_probe(spi);
|
|
||||||
|
|
||||||
if (!info)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
flash = kzalloc(sizeof *flash, GFP_KERNEL);
|
flash = kzalloc(sizeof *flash, GFP_KERNEL);
|
||||||
if (!flash)
|
if (!flash)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
flash->command = kmalloc(MAX_CMD_SIZE + FAST_READ_DUMMY_BYTE, GFP_KERNEL);
|
||||||
|
if (!flash->command) {
|
||||||
|
kfree(flash);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
flash->spi = spi;
|
flash->spi = spi;
|
||||||
mutex_init(&flash->lock);
|
mutex_init(&flash->lock);
|
||||||
dev_set_drvdata(&spi->dev, flash);
|
dev_set_drvdata(&spi->dev, flash);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Atmel serial flash tend to power up
|
* Atmel and SST serial flash tend to power
|
||||||
* with the software protection bits set
|
* up with the software protection bits set
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (info->jedec_id >> 16 == 0x1f) {
|
if (info->jedec_id >> 16 == 0x1f ||
|
||||||
|
info->jedec_id >> 16 == 0xbf) {
|
||||||
write_enable(flash);
|
write_enable(flash);
|
||||||
write_sr(flash, 0);
|
write_sr(flash, 0);
|
||||||
}
|
}
|
||||||
|
@ -812,9 +862,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||||
flash->mtd.erasesize = info->sector_size;
|
flash->mtd.erasesize = info->sector_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
flash->mtd.dev.parent = &spi->dev;
|
if (info->flags & M25P_NO_ERASE)
|
||||||
|
flash->mtd.flags |= MTD_NO_ERASE;
|
||||||
|
|
||||||
dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
|
flash->mtd.dev.parent = &spi->dev;
|
||||||
|
flash->page_size = info->page_size;
|
||||||
|
flash->addr_width = info->addr_width;
|
||||||
|
|
||||||
|
dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name,
|
||||||
(long long)flash->mtd.size >> 10);
|
(long long)flash->mtd.size >> 10);
|
||||||
|
|
||||||
DEBUG(MTD_DEBUG_LEVEL2,
|
DEBUG(MTD_DEBUG_LEVEL2,
|
||||||
|
@ -888,8 +943,10 @@ static int __devexit m25p_remove(struct spi_device *spi)
|
||||||
status = del_mtd_partitions(&flash->mtd);
|
status = del_mtd_partitions(&flash->mtd);
|
||||||
else
|
else
|
||||||
status = del_mtd_device(&flash->mtd);
|
status = del_mtd_device(&flash->mtd);
|
||||||
if (status == 0)
|
if (status == 0) {
|
||||||
|
kfree(flash->command);
|
||||||
kfree(flash);
|
kfree(flash);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,6 +957,7 @@ static struct spi_driver m25p80_driver = {
|
||||||
.bus = &spi_bus_type,
|
.bus = &spi_bus_type,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
},
|
},
|
||||||
|
.id_table = m25p_ids,
|
||||||
.probe = m25p_probe,
|
.probe = m25p_probe,
|
||||||
.remove = __devexit_p(m25p_remove),
|
.remove = __devexit_p(m25p_remove),
|
||||||
|
|
||||||
|
|
|
@ -636,6 +636,7 @@ add_dataflash_otp(struct spi_device *spi, char *name,
|
||||||
struct mtd_info *device;
|
struct mtd_info *device;
|
||||||
struct flash_platform_data *pdata = spi->dev.platform_data;
|
struct flash_platform_data *pdata = spi->dev.platform_data;
|
||||||
char *otp_tag = "";
|
char *otp_tag = "";
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
priv = kzalloc(sizeof *priv, GFP_KERNEL);
|
priv = kzalloc(sizeof *priv, GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
|
@ -693,13 +694,23 @@ add_dataflash_otp(struct spi_device *spi, char *name,
|
||||||
|
|
||||||
if (nr_parts > 0) {
|
if (nr_parts > 0) {
|
||||||
priv->partitioned = 1;
|
priv->partitioned = 1;
|
||||||
return add_mtd_partitions(device, parts, nr_parts);
|
err = add_mtd_partitions(device, parts, nr_parts);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
} else if (pdata && pdata->nr_parts)
|
} else if (pdata && pdata->nr_parts)
|
||||||
dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
|
dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
|
||||||
pdata->nr_parts, device->name);
|
pdata->nr_parts, device->name);
|
||||||
|
|
||||||
return add_mtd_device(device) == 1 ? -ENODEV : 0;
|
if (add_mtd_device(device) == 1)
|
||||||
|
err = -ENODEV;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (!err)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dev_set_drvdata(&spi->dev, NULL);
|
||||||
|
kfree(priv);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int __devinit
|
static inline int __devinit
|
||||||
|
@ -932,8 +943,10 @@ static int __devexit dataflash_remove(struct spi_device *spi)
|
||||||
status = del_mtd_partitions(&flash->mtd);
|
status = del_mtd_partitions(&flash->mtd);
|
||||||
else
|
else
|
||||||
status = del_mtd_device(&flash->mtd);
|
status = del_mtd_device(&flash->mtd);
|
||||||
if (status == 0)
|
if (status == 0) {
|
||||||
|
dev_set_drvdata(&spi->dev, NULL);
|
||||||
kfree(flash);
|
kfree(flash);
|
||||||
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -359,12 +359,6 @@ config MTD_SA1100
|
||||||
the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
|
the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
|
||||||
If you have such a board, say 'Y'.
|
If you have such a board, say 'Y'.
|
||||||
|
|
||||||
config MTD_IPAQ
|
|
||||||
tristate "CFI Flash device mapped on Compaq/HP iPAQ"
|
|
||||||
depends on IPAQ_HANDHELD && MTD_CFI
|
|
||||||
help
|
|
||||||
This provides a driver for the on-board flash of the iPAQ.
|
|
||||||
|
|
||||||
config MTD_DC21285
|
config MTD_DC21285
|
||||||
tristate "CFI Flash device mapped on DC21285 Footbridge"
|
tristate "CFI Flash device mapped on DC21285 Footbridge"
|
||||||
depends on MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS
|
depends on MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS
|
||||||
|
|
|
@ -24,12 +24,12 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o
|
||||||
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
||||||
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
||||||
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
||||||
|
obj-$(CONFIG_MTD_PISMO) += pismo.o
|
||||||
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
|
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
|
||||||
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
|
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
|
||||||
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
|
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
|
||||||
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o
|
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o
|
||||||
obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o
|
obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o
|
||||||
obj-$(CONFIG_MTD_IPAQ) += ipaq-flash.o
|
|
||||||
obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o
|
obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o
|
||||||
obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
|
obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
|
||||||
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
|
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
|
||||||
|
|
|
@ -1,460 +0,0 @@
|
||||||
/*
|
|
||||||
* Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based)
|
|
||||||
*
|
|
||||||
* (C) 2000 Nicolas Pitre <nico@fluxnic.net>
|
|
||||||
* (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com>
|
|
||||||
* (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <asm/page.h>
|
|
||||||
#include <asm/mach-types.h>
|
|
||||||
#include <asm/system.h>
|
|
||||||
#include <asm/errno.h>
|
|
||||||
|
|
||||||
#include <linux/mtd/mtd.h>
|
|
||||||
#include <linux/mtd/map.h>
|
|
||||||
#include <linux/mtd/partitions.h>
|
|
||||||
#ifdef CONFIG_MTD_CONCAT
|
|
||||||
#include <linux/mtd/concat.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <mach/hardware.h>
|
|
||||||
#include <mach/h3600.h>
|
|
||||||
#include <asm/io.h>
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef CONFIG_IPAQ_HANDHELD
|
|
||||||
#error This is for iPAQ Handhelds only
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_SA1100_JORNADA56X
|
|
||||||
|
|
||||||
static void jornada56x_set_vpp(struct map_info *map, int vpp)
|
|
||||||
{
|
|
||||||
if (vpp)
|
|
||||||
GPSR = GPIO_GPIO26;
|
|
||||||
else
|
|
||||||
GPCR = GPIO_GPIO26;
|
|
||||||
GPDR |= GPIO_GPIO26;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SA1100_JORNADA720
|
|
||||||
|
|
||||||
static void jornada720_set_vpp(struct map_info *map, int vpp)
|
|
||||||
{
|
|
||||||
if (vpp)
|
|
||||||
PPSR |= 0x80;
|
|
||||||
else
|
|
||||||
PPSR &= ~0x80;
|
|
||||||
PPDR |= 0x80;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_IPAQ_CS 2 /* Number of CS we are going to test */
|
|
||||||
|
|
||||||
#define IPAQ_MAP_INIT(X) \
|
|
||||||
{ \
|
|
||||||
name: "IPAQ flash " X, \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static struct map_info ipaq_map[MAX_IPAQ_CS] = {
|
|
||||||
IPAQ_MAP_INIT("bank 1"),
|
|
||||||
IPAQ_MAP_INIT("bank 2")
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = {
|
|
||||||
NULL,
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here are partition information for all known IPAQ-based devices.
|
|
||||||
* See include/linux/mtd/partitions.h for definition of the mtd_partition
|
|
||||||
* structure.
|
|
||||||
*
|
|
||||||
* The *_max_flash_size is the maximum possible mapped flash size which
|
|
||||||
* is not necessarily the actual flash size. It must be no more than
|
|
||||||
* the value specified in the "struct map_desc *_io_desc" mapping
|
|
||||||
* definition for the corresponding machine.
|
|
||||||
*
|
|
||||||
* Please keep these in alphabetical order, and formatted as per existing
|
|
||||||
* entries. Thanks.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef CONFIG_IPAQ_HANDHELD
|
|
||||||
static unsigned long h3xxx_max_flash_size = 0x04000000;
|
|
||||||
static struct mtd_partition h3xxx_partitions[] = {
|
|
||||||
{
|
|
||||||
name: "H3XXX boot firmware",
|
|
||||||
#ifndef CONFIG_LAB
|
|
||||||
size: 0x00040000,
|
|
||||||
#else
|
|
||||||
size: 0x00080000,
|
|
||||||
#endif
|
|
||||||
offset: 0,
|
|
||||||
#ifndef CONFIG_LAB
|
|
||||||
mask_flags: MTD_WRITEABLE, /* force read-only */
|
|
||||||
#endif
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "H3XXX root jffs2",
|
|
||||||
#ifndef CONFIG_LAB
|
|
||||||
size: 0x2000000 - 2*0x40000, /* Warning, this is fixed later */
|
|
||||||
offset: 0x00040000,
|
|
||||||
#else
|
|
||||||
size: 0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */
|
|
||||||
offset: 0x00080000,
|
|
||||||
#endif
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "asset",
|
|
||||||
size: 0x40000,
|
|
||||||
offset: 0x2000000 - 0x40000, /* Warning, this is fixed later */
|
|
||||||
mask_flags: MTD_WRITEABLE, /* force read-only */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef CONFIG_MTD_CONCAT
|
|
||||||
static struct mtd_partition h3xxx_partitions_bank2[] = {
|
|
||||||
/* this is used only on 2 CS machines when concat is not present */
|
|
||||||
{
|
|
||||||
name: "second H3XXX root jffs2",
|
|
||||||
size: 0x1000000 - 0x40000, /* Warning, this is fixed later */
|
|
||||||
offset: 0x00000000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "second asset",
|
|
||||||
size: 0x40000,
|
|
||||||
offset: 0x1000000 - 0x40000, /* Warning, this is fixed later */
|
|
||||||
mask_flags: MTD_WRITEABLE, /* force read-only */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(ipaq_vpp_lock);
|
|
||||||
|
|
||||||
static void h3xxx_set_vpp(struct map_info *map, int vpp)
|
|
||||||
{
|
|
||||||
static int nest = 0;
|
|
||||||
|
|
||||||
spin_lock(&ipaq_vpp_lock);
|
|
||||||
if (vpp)
|
|
||||||
nest++;
|
|
||||||
else
|
|
||||||
nest--;
|
|
||||||
if (nest)
|
|
||||||
assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1);
|
|
||||||
else
|
|
||||||
assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0);
|
|
||||||
spin_unlock(&ipaq_vpp_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720)
|
|
||||||
static unsigned long jornada_max_flash_size = 0x02000000;
|
|
||||||
static struct mtd_partition jornada_partitions[] = {
|
|
||||||
{
|
|
||||||
name: "Jornada boot firmware",
|
|
||||||
size: 0x00040000,
|
|
||||||
offset: 0,
|
|
||||||
mask_flags: MTD_WRITEABLE, /* force read-only */
|
|
||||||
}, {
|
|
||||||
name: "Jornada root jffs2",
|
|
||||||
size: MTDPART_SIZ_FULL,
|
|
||||||
offset: 0x00040000,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static struct mtd_partition *parsed_parts;
|
|
||||||
static struct mtd_info *mymtd;
|
|
||||||
|
|
||||||
static unsigned long cs_phys[] = {
|
|
||||||
#ifdef CONFIG_ARCH_SA1100
|
|
||||||
SA1100_CS0_PHYS,
|
|
||||||
SA1100_CS1_PHYS,
|
|
||||||
SA1100_CS2_PHYS,
|
|
||||||
SA1100_CS3_PHYS,
|
|
||||||
SA1100_CS4_PHYS,
|
|
||||||
SA1100_CS5_PHYS,
|
|
||||||
#else
|
|
||||||
PXA_CS0_PHYS,
|
|
||||||
PXA_CS1_PHYS,
|
|
||||||
PXA_CS2_PHYS,
|
|
||||||
PXA_CS3_PHYS,
|
|
||||||
PXA_CS4_PHYS,
|
|
||||||
PXA_CS5_PHYS,
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
|
|
||||||
|
|
||||||
static int __init h1900_special_case(void);
|
|
||||||
|
|
||||||
static int __init ipaq_mtd_init(void)
|
|
||||||
{
|
|
||||||
struct mtd_partition *parts = NULL;
|
|
||||||
int nb_parts = 0;
|
|
||||||
int parsed_nr_parts = 0;
|
|
||||||
const char *part_type;
|
|
||||||
int i; /* used when we have >1 flash chips */
|
|
||||||
unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */
|
|
||||||
|
|
||||||
/* Default flash bankwidth */
|
|
||||||
// ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
|
|
||||||
|
|
||||||
if (machine_is_h1900())
|
|
||||||
{
|
|
||||||
/* For our intents, the h1900 is not a real iPAQ, so we special-case it. */
|
|
||||||
return h1900_special_case();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (machine_is_h3100() || machine_is_h1900())
|
|
||||||
for(i=0; i<MAX_IPAQ_CS; i++)
|
|
||||||
ipaq_map[i].bankwidth = 2;
|
|
||||||
else
|
|
||||||
for(i=0; i<MAX_IPAQ_CS; i++)
|
|
||||||
ipaq_map[i].bankwidth = 4;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Static partition definition selection
|
|
||||||
*/
|
|
||||||
part_type = "static";
|
|
||||||
|
|
||||||
simple_map_init(&ipaq_map[0]);
|
|
||||||
simple_map_init(&ipaq_map[1]);
|
|
||||||
|
|
||||||
#ifdef CONFIG_IPAQ_HANDHELD
|
|
||||||
if (machine_is_ipaq()) {
|
|
||||||
parts = h3xxx_partitions;
|
|
||||||
nb_parts = ARRAY_SIZE(h3xxx_partitions);
|
|
||||||
for(i=0; i<MAX_IPAQ_CS; i++) {
|
|
||||||
ipaq_map[i].size = h3xxx_max_flash_size;
|
|
||||||
ipaq_map[i].set_vpp = h3xxx_set_vpp;
|
|
||||||
ipaq_map[i].phys = cs_phys[i];
|
|
||||||
ipaq_map[i].virt = ioremap(cs_phys[i], 0x04000000);
|
|
||||||
if (machine_is_h3100 () || machine_is_h1900())
|
|
||||||
ipaq_map[i].bankwidth = 2;
|
|
||||||
}
|
|
||||||
if (machine_is_h3600()) {
|
|
||||||
/* No asset partition here */
|
|
||||||
h3xxx_partitions[1].size += 0x40000;
|
|
||||||
nb_parts--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_ARCH_H5400
|
|
||||||
if (machine_is_h5400()) {
|
|
||||||
ipaq_map[0].size = 0x02000000;
|
|
||||||
ipaq_map[1].size = 0x02000000;
|
|
||||||
ipaq_map[1].phys = 0x02000000;
|
|
||||||
ipaq_map[1].virt = ipaq_map[0].virt + 0x02000000;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_ARCH_H1900
|
|
||||||
if (machine_is_h1900()) {
|
|
||||||
ipaq_map[0].size = 0x00400000;
|
|
||||||
ipaq_map[1].size = 0x02000000;
|
|
||||||
ipaq_map[1].phys = 0x00080000;
|
|
||||||
ipaq_map[1].virt = ipaq_map[0].virt + 0x00080000;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SA1100_JORNADA56X
|
|
||||||
if (machine_is_jornada56x()) {
|
|
||||||
parts = jornada_partitions;
|
|
||||||
nb_parts = ARRAY_SIZE(jornada_partitions);
|
|
||||||
ipaq_map[0].size = jornada_max_flash_size;
|
|
||||||
ipaq_map[0].set_vpp = jornada56x_set_vpp;
|
|
||||||
ipaq_map[0].virt = (__u32)ioremap(0x0, 0x04000000);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef CONFIG_SA1100_JORNADA720
|
|
||||||
if (machine_is_jornada720()) {
|
|
||||||
parts = jornada_partitions;
|
|
||||||
nb_parts = ARRAY_SIZE(jornada_partitions);
|
|
||||||
ipaq_map[0].size = jornada_max_flash_size;
|
|
||||||
ipaq_map[0].set_vpp = jornada720_set_vpp;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
if (machine_is_ipaq()) { /* for iPAQs only */
|
|
||||||
for(i=0; i<MAX_IPAQ_CS; i++) {
|
|
||||||
printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with CFI.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
|
|
||||||
my_sub_mtd[i] = do_map_probe("cfi_probe", &ipaq_map[i]);
|
|
||||||
if (!my_sub_mtd[i]) {
|
|
||||||
printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
|
|
||||||
my_sub_mtd[i] = do_map_probe("jedec_probe", &ipaq_map[i]);
|
|
||||||
}
|
|
||||||
if (!my_sub_mtd[i]) {
|
|
||||||
printk(KERN_NOTICE "iPAQ flash: failed to find flash.\n");
|
|
||||||
if (i)
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
return -ENXIO;
|
|
||||||
} else
|
|
||||||
printk(KERN_NOTICE "iPAQ flash: found %d bytes\n", my_sub_mtd[i]->size);
|
|
||||||
|
|
||||||
/* do we really need this debugging? --joshua 20030703 */
|
|
||||||
// printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]);
|
|
||||||
my_sub_mtd[i]->owner = THIS_MODULE;
|
|
||||||
tot_flashsize += my_sub_mtd[i]->size;
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_MTD_CONCAT
|
|
||||||
/* fix the asset location */
|
|
||||||
# ifdef CONFIG_LAB
|
|
||||||
h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */;
|
|
||||||
# else
|
|
||||||
h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000;
|
|
||||||
# endif
|
|
||||||
h3xxx_partitions[2].offset = tot_flashsize - 0x40000;
|
|
||||||
/* and concat the devices */
|
|
||||||
mymtd = mtd_concat_create(&my_sub_mtd[0], i,
|
|
||||||
"ipaq");
|
|
||||||
if (!mymtd) {
|
|
||||||
printk("Cannot create iPAQ concat device\n");
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
mymtd = my_sub_mtd[0];
|
|
||||||
|
|
||||||
/*
|
|
||||||
*In the very near future, command line partition parsing
|
|
||||||
* will use the device name as 'mtd-id' instead of a value
|
|
||||||
* passed to the parse_cmdline_partitions() routine. Since
|
|
||||||
* the bootldr says 'ipaq', make sure it continues to work.
|
|
||||||
*/
|
|
||||||
mymtd->name = "ipaq";
|
|
||||||
|
|
||||||
if ((machine_is_h3600())) {
|
|
||||||
# ifdef CONFIG_LAB
|
|
||||||
h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000;
|
|
||||||
# else
|
|
||||||
h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000;
|
|
||||||
# endif
|
|
||||||
nb_parts = 2;
|
|
||||||
} else {
|
|
||||||
# ifdef CONFIG_LAB
|
|
||||||
h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */
|
|
||||||
# else
|
|
||||||
h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000;
|
|
||||||
# endif
|
|
||||||
h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (my_sub_mtd[1]) {
|
|
||||||
# ifdef CONFIG_LAB
|
|
||||||
h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000;
|
|
||||||
# else
|
|
||||||
h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000;
|
|
||||||
# endif
|
|
||||||
h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/*
|
|
||||||
* Now let's probe for the actual flash. Do it here since
|
|
||||||
* specific machine settings might have been set above.
|
|
||||||
*/
|
|
||||||
printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
|
|
||||||
mymtd = do_map_probe("cfi_probe", &ipaq_map[0]);
|
|
||||||
if (!mymtd)
|
|
||||||
return -ENXIO;
|
|
||||||
mymtd->owner = THIS_MODULE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dynamic partition selection stuff (might override the static ones)
|
|
||||||
*/
|
|
||||||
|
|
||||||
i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0);
|
|
||||||
|
|
||||||
if (i > 0) {
|
|
||||||
nb_parts = parsed_nr_parts = i;
|
|
||||||
parts = parsed_parts;
|
|
||||||
part_type = "dynamic";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parts) {
|
|
||||||
printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n");
|
|
||||||
add_mtd_device(mymtd);
|
|
||||||
#ifndef CONFIG_MTD_CONCAT
|
|
||||||
if (my_sub_mtd[1])
|
|
||||||
add_mtd_device(my_sub_mtd[1]);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
|
|
||||||
add_mtd_partitions(mymtd, parts, nb_parts);
|
|
||||||
#ifndef CONFIG_MTD_CONCAT
|
|
||||||
if (my_sub_mtd[1])
|
|
||||||
add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit ipaq_mtd_cleanup(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (mymtd) {
|
|
||||||
del_mtd_partitions(mymtd);
|
|
||||||
#ifndef CONFIG_MTD_CONCAT
|
|
||||||
if (my_sub_mtd[1])
|
|
||||||
del_mtd_partitions(my_sub_mtd[1]);
|
|
||||||
#endif
|
|
||||||
map_destroy(mymtd);
|
|
||||||
#ifdef CONFIG_MTD_CONCAT
|
|
||||||
for(i=0; i<MAX_IPAQ_CS; i++)
|
|
||||||
#else
|
|
||||||
for(i=1; i<MAX_IPAQ_CS; i++)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (my_sub_mtd[i])
|
|
||||||
map_destroy(my_sub_mtd[i]);
|
|
||||||
}
|
|
||||||
kfree(parsed_parts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init h1900_special_case(void)
|
|
||||||
{
|
|
||||||
/* The iPAQ h1900 is a special case - it has weird ROM. */
|
|
||||||
simple_map_init(&ipaq_map[0]);
|
|
||||||
ipaq_map[0].size = 0x80000;
|
|
||||||
ipaq_map[0].set_vpp = h3xxx_set_vpp;
|
|
||||||
ipaq_map[0].phys = 0x0;
|
|
||||||
ipaq_map[0].virt = ioremap(0x0, 0x04000000);
|
|
||||||
ipaq_map[0].bankwidth = 2;
|
|
||||||
|
|
||||||
printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
|
|
||||||
mymtd = do_map_probe("jedec_probe", &ipaq_map[0]);
|
|
||||||
if (!mymtd)
|
|
||||||
return -ENODEV;
|
|
||||||
add_mtd_device(mymtd);
|
|
||||||
printk(KERN_NOTICE "iPAQ flash: registered h1910 flash\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
module_init(ipaq_mtd_init);
|
|
||||||
module_exit(ipaq_mtd_cleanup);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Jamey Hicks");
|
|
||||||
MODULE_DESCRIPTION("IPAQ CFI map driver");
|
|
||||||
MODULE_LICENSE("MIT");
|
|
|
@ -210,7 +210,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||||
* not attempt to do a direct access on us.
|
* not attempt to do a direct access on us.
|
||||||
*/
|
*/
|
||||||
info->map.phys = NO_XIP;
|
info->map.phys = NO_XIP;
|
||||||
info->map.size = dev->resource->end - dev->resource->start + 1;
|
info->map.size = resource_size(dev->resource);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only support 16-bit accesses for now. If and when
|
* We only support 16-bit accesses for now. If and when
|
||||||
|
@ -224,7 +224,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||||
info->map.copy_from = ixp4xx_copy_from,
|
info->map.copy_from = ixp4xx_copy_from,
|
||||||
|
|
||||||
info->res = request_mem_region(dev->resource->start,
|
info->res = request_mem_region(dev->resource->start,
|
||||||
dev->resource->end - dev->resource->start + 1,
|
resource_size(dev->resource),
|
||||||
"IXP4XXFlash");
|
"IXP4XXFlash");
|
||||||
if (!info->res) {
|
if (!info->res) {
|
||||||
printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n");
|
printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n");
|
||||||
|
@ -233,7 +233,7 @@ static int ixp4xx_flash_probe(struct platform_device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
info->map.virt = ioremap(dev->resource->start,
|
info->map.virt = ioremap(dev->resource->start,
|
||||||
dev->resource->end - dev->resource->start + 1);
|
resource_size(dev->resource));
|
||||||
if (!info->map.virt) {
|
if (!info->map.virt) {
|
||||||
printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n");
|
printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n");
|
||||||
err = -EIO;
|
err = -EIO;
|
||||||
|
|
|
@ -48,23 +48,22 @@ static int physmap_flash_remove(struct platform_device *dev)
|
||||||
|
|
||||||
if (info->cmtd) {
|
if (info->cmtd) {
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
if (info->nr_parts || physmap_data->nr_parts)
|
if (info->nr_parts || physmap_data->nr_parts) {
|
||||||
del_mtd_partitions(info->cmtd);
|
del_mtd_partitions(info->cmtd);
|
||||||
else
|
|
||||||
|
if (info->nr_parts)
|
||||||
|
kfree(info->parts);
|
||||||
|
} else {
|
||||||
del_mtd_device(info->cmtd);
|
del_mtd_device(info->cmtd);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
del_mtd_device(info->cmtd);
|
del_mtd_device(info->cmtd);
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
|
||||||
if (info->nr_parts)
|
|
||||||
kfree(info->parts);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_CONCAT
|
#ifdef CONFIG_MTD_CONCAT
|
||||||
if (info->cmtd != info->mtd[0])
|
if (info->cmtd != info->mtd[0])
|
||||||
mtd_concat_destroy(info->cmtd);
|
mtd_concat_destroy(info->cmtd);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < MAX_RESOURCES; i++) {
|
for (i = 0; i < MAX_RESOURCES; i++) {
|
||||||
if (info->mtd[i] != NULL)
|
if (info->mtd[i] != NULL)
|
||||||
|
@ -130,7 +129,7 @@ static int physmap_flash_probe(struct platform_device *dev)
|
||||||
info->map[i].size);
|
info->map[i].size);
|
||||||
if (info->map[i].virt == NULL) {
|
if (info->map[i].virt == NULL) {
|
||||||
dev_err(&dev->dev, "Failed to ioremap flash region\n");
|
dev_err(&dev->dev, "Failed to ioremap flash region\n");
|
||||||
err = EIO;
|
err = -EIO;
|
||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *pla
|
||||||
plat->exit();
|
plat->exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sa_info *__init
|
static struct sa_info *__devinit
|
||||||
sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
|
sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
|
||||||
{
|
{
|
||||||
struct sa_info *info;
|
struct sa_info *info;
|
||||||
|
|
|
@ -612,16 +612,15 @@ static int __devinit vmu_connect(struct maple_device *mdev)
|
||||||
|
|
||||||
test_flash_data = be32_to_cpu(mdev->devinfo.function);
|
test_flash_data = be32_to_cpu(mdev->devinfo.function);
|
||||||
/* Need to count how many bits are set - to find out which
|
/* Need to count how many bits are set - to find out which
|
||||||
* function_data element has details of the memory card:
|
* function_data element has details of the memory card
|
||||||
* using Brian Kernighan's/Peter Wegner's method */
|
*/
|
||||||
for (c = 0; test_flash_data; c++)
|
c = hweight_long(test_flash_data);
|
||||||
test_flash_data &= test_flash_data - 1;
|
|
||||||
|
|
||||||
basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
|
basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
|
||||||
|
|
||||||
card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
|
card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
|
||||||
if (!card) {
|
if (!card) {
|
||||||
error = ENOMEM;
|
error = -ENOMEM;
|
||||||
goto fail_nomem;
|
goto fail_nomem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,9 +84,6 @@ static int mtd_blktrans_thread(void *arg)
|
||||||
struct request_queue *rq = tr->blkcore_priv->rq;
|
struct request_queue *rq = tr->blkcore_priv->rq;
|
||||||
struct request *req = NULL;
|
struct request *req = NULL;
|
||||||
|
|
||||||
/* we might get involved when memory gets low, so use PF_MEMALLOC */
|
|
||||||
current->flags |= PF_MEMALLOC;
|
|
||||||
|
|
||||||
spin_lock_irq(rq->queue_lock);
|
spin_lock_irq(rq->queue_lock);
|
||||||
|
|
||||||
while (!kthread_should_stop()) {
|
while (!kthread_should_stop()) {
|
||||||
|
@ -381,7 +378,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
|
||||||
tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
|
tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
|
||||||
"%sd", tr->name);
|
"%sd", tr->name);
|
||||||
if (IS_ERR(tr->blkcore_priv->thread)) {
|
if (IS_ERR(tr->blkcore_priv->thread)) {
|
||||||
int ret = PTR_ERR(tr->blkcore_priv->thread);
|
ret = PTR_ERR(tr->blkcore_priv->thread);
|
||||||
blk_cleanup_queue(tr->blkcore_priv->rq);
|
blk_cleanup_queue(tr->blkcore_priv->rq);
|
||||||
unregister_blkdev(tr->major, tr->name);
|
unregister_blkdev(tr->major, tr->name);
|
||||||
kfree(tr->blkcore_priv);
|
kfree(tr->blkcore_priv);
|
||||||
|
|
|
@ -447,7 +447,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
|
||||||
for (i=0; i< MAX_MTD_DEVICES; i++)
|
for (i=0; i< MAX_MTD_DEVICES; i++)
|
||||||
if (mtd_table[i] == mtd)
|
if (mtd_table[i] == mtd)
|
||||||
ret = mtd_table[i];
|
ret = mtd_table[i];
|
||||||
} else if (num < MAX_MTD_DEVICES) {
|
} else if (num >= 0 && num < MAX_MTD_DEVICES) {
|
||||||
ret = mtd_table[num];
|
ret = mtd_table[num];
|
||||||
if (mtd && mtd != ret)
|
if (mtd && mtd != ret)
|
||||||
ret = NULL;
|
ret = NULL;
|
||||||
|
|
|
@ -29,14 +29,34 @@
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/kmsg_dump.h>
|
||||||
|
|
||||||
|
/* Maximum MTD partition size */
|
||||||
|
#define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
|
||||||
|
|
||||||
#define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
|
#define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
|
||||||
#define OOPS_PAGE_SIZE 4096
|
#define MTDOOPS_HEADER_SIZE 8
|
||||||
|
|
||||||
|
static unsigned long record_size = 4096;
|
||||||
|
module_param(record_size, ulong, 0400);
|
||||||
|
MODULE_PARM_DESC(record_size,
|
||||||
|
"record size for MTD OOPS pages in bytes (default 4096)");
|
||||||
|
|
||||||
|
static char mtddev[80];
|
||||||
|
module_param_string(mtddev, mtddev, 80, 0400);
|
||||||
|
MODULE_PARM_DESC(mtddev,
|
||||||
|
"name or index number of the MTD device to use");
|
||||||
|
|
||||||
|
static int dump_oops = 1;
|
||||||
|
module_param(dump_oops, int, 0600);
|
||||||
|
MODULE_PARM_DESC(dump_oops,
|
||||||
|
"set to 1 to dump oopses, 0 to only dump panics (default 1)");
|
||||||
|
|
||||||
static struct mtdoops_context {
|
static struct mtdoops_context {
|
||||||
|
struct kmsg_dumper dump;
|
||||||
|
|
||||||
int mtd_index;
|
int mtd_index;
|
||||||
struct work_struct work_erase;
|
struct work_struct work_erase;
|
||||||
struct work_struct work_write;
|
struct work_struct work_write;
|
||||||
|
@ -44,28 +64,43 @@ static struct mtdoops_context {
|
||||||
int oops_pages;
|
int oops_pages;
|
||||||
int nextpage;
|
int nextpage;
|
||||||
int nextcount;
|
int nextcount;
|
||||||
char *name;
|
unsigned long *oops_page_used;
|
||||||
|
|
||||||
void *oops_buf;
|
void *oops_buf;
|
||||||
|
|
||||||
/* writecount and disabling ready are spin lock protected */
|
|
||||||
spinlock_t writecount_lock;
|
|
||||||
int ready;
|
|
||||||
int writecount;
|
|
||||||
} oops_cxt;
|
} oops_cxt;
|
||||||
|
|
||||||
|
static void mark_page_used(struct mtdoops_context *cxt, int page)
|
||||||
|
{
|
||||||
|
set_bit(page, cxt->oops_page_used);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mark_page_unused(struct mtdoops_context *cxt, int page)
|
||||||
|
{
|
||||||
|
clear_bit(page, cxt->oops_page_used);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int page_is_used(struct mtdoops_context *cxt, int page)
|
||||||
|
{
|
||||||
|
return test_bit(page, cxt->oops_page_used);
|
||||||
|
}
|
||||||
|
|
||||||
static void mtdoops_erase_callback(struct erase_info *done)
|
static void mtdoops_erase_callback(struct erase_info *done)
|
||||||
{
|
{
|
||||||
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
|
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
|
||||||
wake_up(wait_q);
|
wake_up(wait_q);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
|
static int mtdoops_erase_block(struct mtdoops_context *cxt, int offset)
|
||||||
{
|
{
|
||||||
|
struct mtd_info *mtd = cxt->mtd;
|
||||||
|
u32 start_page_offset = mtd_div_by_eb(offset, mtd) * mtd->erasesize;
|
||||||
|
u32 start_page = start_page_offset / record_size;
|
||||||
|
u32 erase_pages = mtd->erasesize / record_size;
|
||||||
struct erase_info erase;
|
struct erase_info erase;
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
DECLARE_WAITQUEUE(wait, current);
|
||||||
wait_queue_head_t wait_q;
|
wait_queue_head_t wait_q;
|
||||||
int ret;
|
int ret;
|
||||||
|
int page;
|
||||||
|
|
||||||
init_waitqueue_head(&wait_q);
|
init_waitqueue_head(&wait_q);
|
||||||
erase.mtd = mtd;
|
erase.mtd = mtd;
|
||||||
|
@ -81,25 +116,24 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
|
||||||
if (ret) {
|
if (ret) {
|
||||||
set_current_state(TASK_RUNNING);
|
set_current_state(TASK_RUNNING);
|
||||||
remove_wait_queue(&wait_q, &wait);
|
remove_wait_queue(&wait_q, &wait);
|
||||||
printk (KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] "
|
printk(KERN_WARNING "mtdoops: erase of region [0x%llx, 0x%llx] on \"%s\" failed\n",
|
||||||
"on \"%s\" failed\n",
|
(unsigned long long)erase.addr,
|
||||||
(unsigned long long)erase.addr, (unsigned long long)erase.len, mtd->name);
|
(unsigned long long)erase.len, mtddev);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule(); /* Wait for erase to finish. */
|
schedule(); /* Wait for erase to finish. */
|
||||||
remove_wait_queue(&wait_q, &wait);
|
remove_wait_queue(&wait_q, &wait);
|
||||||
|
|
||||||
|
/* Mark pages as unused */
|
||||||
|
for (page = start_page; page < start_page + erase_pages; page++)
|
||||||
|
mark_page_unused(cxt, page);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtdoops_inc_counter(struct mtdoops_context *cxt)
|
static void mtdoops_inc_counter(struct mtdoops_context *cxt)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = cxt->mtd;
|
|
||||||
size_t retlen;
|
|
||||||
u32 count;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
cxt->nextpage++;
|
cxt->nextpage++;
|
||||||
if (cxt->nextpage >= cxt->oops_pages)
|
if (cxt->nextpage >= cxt->oops_pages)
|
||||||
cxt->nextpage = 0;
|
cxt->nextpage = 0;
|
||||||
|
@ -107,25 +141,13 @@ static void mtdoops_inc_counter(struct mtdoops_context *cxt)
|
||||||
if (cxt->nextcount == 0xffffffff)
|
if (cxt->nextcount == 0xffffffff)
|
||||||
cxt->nextcount = 0;
|
cxt->nextcount = 0;
|
||||||
|
|
||||||
ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4,
|
if (page_is_used(cxt, cxt->nextpage)) {
|
||||||
&retlen, (u_char *) &count);
|
|
||||||
if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) {
|
|
||||||
printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)"
|
|
||||||
", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE,
|
|
||||||
retlen, ret);
|
|
||||||
schedule_work(&cxt->work_erase);
|
schedule_work(&cxt->work_erase);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See if we need to erase the next block */
|
printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
|
||||||
if (count != 0xffffffff) {
|
cxt->nextpage, cxt->nextcount);
|
||||||
schedule_work(&cxt->work_erase);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n",
|
|
||||||
cxt->nextpage, cxt->nextcount);
|
|
||||||
cxt->ready = 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scheduled work - when we can't proceed without erasing a block */
|
/* Scheduled work - when we can't proceed without erasing a block */
|
||||||
|
@ -140,47 +162,47 @@ static void mtdoops_workfunc_erase(struct work_struct *work)
|
||||||
if (!mtd)
|
if (!mtd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize;
|
mod = (cxt->nextpage * record_size) % mtd->erasesize;
|
||||||
if (mod != 0) {
|
if (mod != 0) {
|
||||||
cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE);
|
cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / record_size);
|
||||||
if (cxt->nextpage >= cxt->oops_pages)
|
if (cxt->nextpage >= cxt->oops_pages)
|
||||||
cxt->nextpage = 0;
|
cxt->nextpage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (mtd->block_isbad) {
|
while (mtd->block_isbad) {
|
||||||
ret = mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
|
ret = mtd->block_isbad(mtd, cxt->nextpage * record_size);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
break;
|
break;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk(KERN_ERR "mtdoops: block_isbad failed, aborting.\n");
|
printk(KERN_ERR "mtdoops: block_isbad failed, aborting\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
badblock:
|
badblock:
|
||||||
printk(KERN_WARNING "mtdoops: Bad block at %08x\n",
|
printk(KERN_WARNING "mtdoops: bad block at %08lx\n",
|
||||||
cxt->nextpage * OOPS_PAGE_SIZE);
|
cxt->nextpage * record_size);
|
||||||
i++;
|
i++;
|
||||||
cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE);
|
cxt->nextpage = cxt->nextpage + (mtd->erasesize / record_size);
|
||||||
if (cxt->nextpage >= cxt->oops_pages)
|
if (cxt->nextpage >= cxt->oops_pages)
|
||||||
cxt->nextpage = 0;
|
cxt->nextpage = 0;
|
||||||
if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) {
|
if (i == cxt->oops_pages / (mtd->erasesize / record_size)) {
|
||||||
printk(KERN_ERR "mtdoops: All blocks bad!\n");
|
printk(KERN_ERR "mtdoops: all blocks bad!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
|
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
|
||||||
ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
|
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
|
||||||
|
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
|
printk(KERN_DEBUG "mtdoops: ready %d, %d\n",
|
||||||
cxt->ready = 1;
|
cxt->nextpage, cxt->nextcount);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtd->block_markbad && (ret == -EIO)) {
|
if (mtd->block_markbad && ret == -EIO) {
|
||||||
ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
|
ret = mtd->block_markbad(mtd, cxt->nextpage * record_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n");
|
printk(KERN_ERR "mtdoops: block_markbad failed, aborting\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,36 +213,37 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
|
||||||
{
|
{
|
||||||
struct mtd_info *mtd = cxt->mtd;
|
struct mtd_info *mtd = cxt->mtd;
|
||||||
size_t retlen;
|
size_t retlen;
|
||||||
|
u32 *hdr;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (cxt->writecount < OOPS_PAGE_SIZE)
|
/* Add mtdoops header to the buffer */
|
||||||
memset(cxt->oops_buf + cxt->writecount, 0xff,
|
hdr = cxt->oops_buf;
|
||||||
OOPS_PAGE_SIZE - cxt->writecount);
|
hdr[0] = cxt->nextcount;
|
||||||
|
hdr[1] = MTDOOPS_KERNMSG_MAGIC;
|
||||||
|
|
||||||
if (panic)
|
if (panic)
|
||||||
ret = mtd->panic_write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
|
ret = mtd->panic_write(mtd, cxt->nextpage * record_size,
|
||||||
OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
|
record_size, &retlen, cxt->oops_buf);
|
||||||
else
|
else
|
||||||
ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
|
ret = mtd->write(mtd, cxt->nextpage * record_size,
|
||||||
OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
|
record_size, &retlen, cxt->oops_buf);
|
||||||
|
|
||||||
cxt->writecount = 0;
|
if (retlen != record_size || ret < 0)
|
||||||
|
printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
|
||||||
if ((retlen != OOPS_PAGE_SIZE) || (ret < 0))
|
cxt->nextpage * record_size, retlen, record_size, ret);
|
||||||
printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n",
|
mark_page_used(cxt, cxt->nextpage);
|
||||||
cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret);
|
memset(cxt->oops_buf, 0xff, record_size);
|
||||||
|
|
||||||
mtdoops_inc_counter(cxt);
|
mtdoops_inc_counter(cxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void mtdoops_workfunc_write(struct work_struct *work)
|
static void mtdoops_workfunc_write(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct mtdoops_context *cxt =
|
struct mtdoops_context *cxt =
|
||||||
container_of(work, struct mtdoops_context, work_write);
|
container_of(work, struct mtdoops_context, work_write);
|
||||||
|
|
||||||
mtdoops_write(cxt, 0);
|
mtdoops_write(cxt, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void find_next_position(struct mtdoops_context *cxt)
|
static void find_next_position(struct mtdoops_context *cxt)
|
||||||
{
|
{
|
||||||
|
@ -230,28 +253,33 @@ static void find_next_position(struct mtdoops_context *cxt)
|
||||||
size_t retlen;
|
size_t retlen;
|
||||||
|
|
||||||
for (page = 0; page < cxt->oops_pages; page++) {
|
for (page = 0; page < cxt->oops_pages; page++) {
|
||||||
ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 8, &retlen, (u_char *) &count[0]);
|
/* Assume the page is used */
|
||||||
if ((retlen != 8) || ((ret < 0) && (ret != -EUCLEAN))) {
|
mark_page_used(cxt, page);
|
||||||
printk(KERN_ERR "mtdoops: Read failure at %d (%td of 8 read)"
|
ret = mtd->read(mtd, page * record_size, MTDOOPS_HEADER_SIZE,
|
||||||
", err %d.\n", page * OOPS_PAGE_SIZE, retlen, ret);
|
&retlen, (u_char *) &count[0]);
|
||||||
|
if (retlen != MTDOOPS_HEADER_SIZE ||
|
||||||
|
(ret < 0 && ret != -EUCLEAN)) {
|
||||||
|
printk(KERN_ERR "mtdoops: read failure at %ld (%td of %d read), err %d\n",
|
||||||
|
page * record_size, retlen,
|
||||||
|
MTDOOPS_HEADER_SIZE, ret);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count[1] != MTDOOPS_KERNMSG_MAGIC)
|
if (count[0] == 0xffffffff && count[1] == 0xffffffff)
|
||||||
continue;
|
mark_page_unused(cxt, page);
|
||||||
if (count[0] == 0xffffffff)
|
if (count[0] == 0xffffffff)
|
||||||
continue;
|
continue;
|
||||||
if (maxcount == 0xffffffff) {
|
if (maxcount == 0xffffffff) {
|
||||||
maxcount = count[0];
|
maxcount = count[0];
|
||||||
maxpos = page;
|
maxpos = page;
|
||||||
} else if ((count[0] < 0x40000000) && (maxcount > 0xc0000000)) {
|
} else if (count[0] < 0x40000000 && maxcount > 0xc0000000) {
|
||||||
maxcount = count[0];
|
maxcount = count[0];
|
||||||
maxpos = page;
|
maxpos = page;
|
||||||
} else if ((count[0] > maxcount) && (count[0] < 0xc0000000)) {
|
} else if (count[0] > maxcount && count[0] < 0xc0000000) {
|
||||||
maxcount = count[0];
|
maxcount = count[0];
|
||||||
maxpos = page;
|
maxpos = page;
|
||||||
} else if ((count[0] > maxcount) && (count[0] > 0xc0000000)
|
} else if (count[0] > maxcount && count[0] > 0xc0000000
|
||||||
&& (maxcount > 0x80000000)) {
|
&& maxcount > 0x80000000) {
|
||||||
maxcount = count[0];
|
maxcount = count[0];
|
||||||
maxpos = page;
|
maxpos = page;
|
||||||
}
|
}
|
||||||
|
@ -269,37 +297,91 @@ static void find_next_position(struct mtdoops_context *cxt)
|
||||||
mtdoops_inc_counter(cxt);
|
mtdoops_inc_counter(cxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
|
||||||
|
enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
|
||||||
|
const char *s2, unsigned long l2)
|
||||||
|
{
|
||||||
|
struct mtdoops_context *cxt = container_of(dumper,
|
||||||
|
struct mtdoops_context, dump);
|
||||||
|
unsigned long s1_start, s2_start;
|
||||||
|
unsigned long l1_cpy, l2_cpy;
|
||||||
|
char *dst;
|
||||||
|
|
||||||
|
/* Only dump oopses if dump_oops is set */
|
||||||
|
if (reason == KMSG_DUMP_OOPS && !dump_oops)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dst = cxt->oops_buf + MTDOOPS_HEADER_SIZE; /* Skip the header */
|
||||||
|
l2_cpy = min(l2, record_size - MTDOOPS_HEADER_SIZE);
|
||||||
|
l1_cpy = min(l1, record_size - MTDOOPS_HEADER_SIZE - l2_cpy);
|
||||||
|
|
||||||
|
s2_start = l2 - l2_cpy;
|
||||||
|
s1_start = l1 - l1_cpy;
|
||||||
|
|
||||||
|
memcpy(dst, s1 + s1_start, l1_cpy);
|
||||||
|
memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy);
|
||||||
|
|
||||||
|
/* Panics must be written immediately */
|
||||||
|
if (reason == KMSG_DUMP_PANIC) {
|
||||||
|
if (!cxt->mtd->panic_write)
|
||||||
|
printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n");
|
||||||
|
else
|
||||||
|
mtdoops_write(cxt, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For other cases, schedule work to write it "nicely" */
|
||||||
|
schedule_work(&cxt->work_write);
|
||||||
|
}
|
||||||
|
|
||||||
static void mtdoops_notify_add(struct mtd_info *mtd)
|
static void mtdoops_notify_add(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct mtdoops_context *cxt = &oops_cxt;
|
struct mtdoops_context *cxt = &oops_cxt;
|
||||||
|
u64 mtdoops_pages = div_u64(mtd->size, record_size);
|
||||||
|
int err;
|
||||||
|
|
||||||
if (cxt->name && !strcmp(mtd->name, cxt->name))
|
if (!strcmp(mtd->name, mtddev))
|
||||||
cxt->mtd_index = mtd->index;
|
cxt->mtd_index = mtd->index;
|
||||||
|
|
||||||
if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
|
if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mtd->size < (mtd->erasesize * 2)) {
|
if (mtd->size < mtd->erasesize * 2) {
|
||||||
printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n",
|
printk(KERN_ERR "mtdoops: MTD partition %d not big enough for mtdoops\n",
|
||||||
mtd->index);
|
mtd->index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mtd->erasesize < record_size) {
|
||||||
|
printk(KERN_ERR "mtdoops: eraseblock size of MTD partition %d too small\n",
|
||||||
|
mtd->index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mtd->size > MTDOOPS_MAX_MTD_SIZE) {
|
||||||
|
printk(KERN_ERR "mtdoops: mtd%d is too large (limit is %d MiB)\n",
|
||||||
|
mtd->index, MTDOOPS_MAX_MTD_SIZE / 1024 / 1024);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mtd->erasesize < OOPS_PAGE_SIZE) {
|
/* oops_page_used is a bit field */
|
||||||
printk(KERN_ERR "Eraseblock size of MTD partition %d too small\n",
|
cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages,
|
||||||
mtd->index);
|
BITS_PER_LONG));
|
||||||
|
if (!cxt->oops_page_used) {
|
||||||
|
printk(KERN_ERR "mtdoops: could not allocate page array\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cxt->dump.dump = mtdoops_do_dump;
|
||||||
|
err = kmsg_dump_register(&cxt->dump);
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
|
||||||
|
vfree(cxt->oops_page_used);
|
||||||
|
cxt->oops_page_used = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cxt->mtd = mtd;
|
cxt->mtd = mtd;
|
||||||
if (mtd->size > INT_MAX)
|
cxt->oops_pages = (int)mtd->size / record_size;
|
||||||
cxt->oops_pages = INT_MAX / OOPS_PAGE_SIZE;
|
|
||||||
else
|
|
||||||
cxt->oops_pages = (int)mtd->size / OOPS_PAGE_SIZE;
|
|
||||||
|
|
||||||
find_next_position(cxt);
|
find_next_position(cxt);
|
||||||
|
|
||||||
printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
|
printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,149 +389,78 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
struct mtdoops_context *cxt = &oops_cxt;
|
struct mtdoops_context *cxt = &oops_cxt;
|
||||||
|
|
||||||
if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
|
if (mtd->index != cxt->mtd_index || cxt->mtd_index < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (kmsg_dump_unregister(&cxt->dump) < 0)
|
||||||
|
printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
|
||||||
|
|
||||||
cxt->mtd = NULL;
|
cxt->mtd = NULL;
|
||||||
flush_scheduled_work();
|
flush_scheduled_work();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtdoops_console_sync(void)
|
|
||||||
{
|
|
||||||
struct mtdoops_context *cxt = &oops_cxt;
|
|
||||||
struct mtd_info *mtd = cxt->mtd;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if (!cxt->ready || !mtd || cxt->writecount == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Once ready is 0 and we've held the lock no further writes to the
|
|
||||||
* buffer will happen
|
|
||||||
*/
|
|
||||||
spin_lock_irqsave(&cxt->writecount_lock, flags);
|
|
||||||
if (!cxt->ready) {
|
|
||||||
spin_unlock_irqrestore(&cxt->writecount_lock, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cxt->ready = 0;
|
|
||||||
spin_unlock_irqrestore(&cxt->writecount_lock, flags);
|
|
||||||
|
|
||||||
if (mtd->panic_write && in_interrupt())
|
|
||||||
/* Interrupt context, we're going to panic so try and log */
|
|
||||||
mtdoops_write(cxt, 1);
|
|
||||||
else
|
|
||||||
schedule_work(&cxt->work_write);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mtdoops_console_write(struct console *co, const char *s, unsigned int count)
|
|
||||||
{
|
|
||||||
struct mtdoops_context *cxt = co->data;
|
|
||||||
struct mtd_info *mtd = cxt->mtd;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
if (!oops_in_progress) {
|
|
||||||
mtdoops_console_sync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cxt->ready || !mtd)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Locking on writecount ensures sequential writes to the buffer */
|
|
||||||
spin_lock_irqsave(&cxt->writecount_lock, flags);
|
|
||||||
|
|
||||||
/* Check ready status didn't change whilst waiting for the lock */
|
|
||||||
if (!cxt->ready) {
|
|
||||||
spin_unlock_irqrestore(&cxt->writecount_lock, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cxt->writecount == 0) {
|
|
||||||
u32 *stamp = cxt->oops_buf;
|
|
||||||
*stamp++ = cxt->nextcount;
|
|
||||||
*stamp = MTDOOPS_KERNMSG_MAGIC;
|
|
||||||
cxt->writecount = 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((count + cxt->writecount) > OOPS_PAGE_SIZE)
|
|
||||||
count = OOPS_PAGE_SIZE - cxt->writecount;
|
|
||||||
|
|
||||||
memcpy(cxt->oops_buf + cxt->writecount, s, count);
|
|
||||||
cxt->writecount += count;
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&cxt->writecount_lock, flags);
|
|
||||||
|
|
||||||
if (cxt->writecount == OOPS_PAGE_SIZE)
|
|
||||||
mtdoops_console_sync();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init mtdoops_console_setup(struct console *co, char *options)
|
|
||||||
{
|
|
||||||
struct mtdoops_context *cxt = co->data;
|
|
||||||
|
|
||||||
if (cxt->mtd_index != -1 || cxt->name)
|
|
||||||
return -EBUSY;
|
|
||||||
if (options) {
|
|
||||||
cxt->name = kstrdup(options, GFP_KERNEL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (co->index == -1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
cxt->mtd_index = co->index;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct mtd_notifier mtdoops_notifier = {
|
static struct mtd_notifier mtdoops_notifier = {
|
||||||
.add = mtdoops_notify_add,
|
.add = mtdoops_notify_add,
|
||||||
.remove = mtdoops_notify_remove,
|
.remove = mtdoops_notify_remove,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct console mtdoops_console = {
|
static int __init mtdoops_init(void)
|
||||||
.name = "ttyMTD",
|
|
||||||
.write = mtdoops_console_write,
|
|
||||||
.setup = mtdoops_console_setup,
|
|
||||||
.unblank = mtdoops_console_sync,
|
|
||||||
.index = -1,
|
|
||||||
.data = &oops_cxt,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __init mtdoops_console_init(void)
|
|
||||||
{
|
{
|
||||||
struct mtdoops_context *cxt = &oops_cxt;
|
struct mtdoops_context *cxt = &oops_cxt;
|
||||||
|
int mtd_index;
|
||||||
|
char *endp;
|
||||||
|
|
||||||
|
if (strlen(mtddev) == 0) {
|
||||||
|
printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if ((record_size & 4095) != 0) {
|
||||||
|
printk(KERN_ERR "mtdoops: record_size must be a multiple of 4096\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (record_size < 4096) {
|
||||||
|
printk(KERN_ERR "mtdoops: record_size must be over 4096 bytes\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the MTD device to use */
|
||||||
cxt->mtd_index = -1;
|
cxt->mtd_index = -1;
|
||||||
cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
|
mtd_index = simple_strtoul(mtddev, &endp, 0);
|
||||||
spin_lock_init(&cxt->writecount_lock);
|
if (*endp == '\0')
|
||||||
|
cxt->mtd_index = mtd_index;
|
||||||
|
if (cxt->mtd_index > MAX_MTD_DEVICES) {
|
||||||
|
printk(KERN_ERR "mtdoops: invalid mtd device number (%u) given\n",
|
||||||
|
mtd_index);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cxt->oops_buf = vmalloc(record_size);
|
||||||
if (!cxt->oops_buf) {
|
if (!cxt->oops_buf) {
|
||||||
printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n");
|
printk(KERN_ERR "mtdoops: failed to allocate buffer workspace\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
memset(cxt->oops_buf, 0xff, record_size);
|
||||||
|
|
||||||
INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
|
INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
|
||||||
INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
|
INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
|
||||||
|
|
||||||
register_console(&mtdoops_console);
|
|
||||||
register_mtd_user(&mtdoops_notifier);
|
register_mtd_user(&mtdoops_notifier);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit mtdoops_console_exit(void)
|
static void __exit mtdoops_exit(void)
|
||||||
{
|
{
|
||||||
struct mtdoops_context *cxt = &oops_cxt;
|
struct mtdoops_context *cxt = &oops_cxt;
|
||||||
|
|
||||||
unregister_mtd_user(&mtdoops_notifier);
|
unregister_mtd_user(&mtdoops_notifier);
|
||||||
unregister_console(&mtdoops_console);
|
|
||||||
kfree(cxt->name);
|
|
||||||
vfree(cxt->oops_buf);
|
vfree(cxt->oops_buf);
|
||||||
|
vfree(cxt->oops_page_used);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
subsys_initcall(mtdoops_console_init);
|
module_init(mtdoops_init);
|
||||||
module_exit(mtdoops_console_exit);
|
module_exit(mtdoops_exit);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
|
MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
|
||||||
|
|
|
@ -201,6 +201,22 @@ config MTD_NAND_S3C2410_CLKSTOP
|
||||||
when the is NAND chip selected or released, but will save
|
when the is NAND chip selected or released, but will save
|
||||||
approximately 5mA of power when there is nothing happening.
|
approximately 5mA of power when there is nothing happening.
|
||||||
|
|
||||||
|
config MTD_NAND_BCM_UMI
|
||||||
|
tristate "NAND Flash support for BCM Reference Boards"
|
||||||
|
depends on ARCH_BCMRING && MTD_NAND
|
||||||
|
help
|
||||||
|
This enables the NAND flash controller on the BCM UMI block.
|
||||||
|
|
||||||
|
No board specfic support is done by this driver, each board
|
||||||
|
must advertise a platform_device for the driver to attach.
|
||||||
|
|
||||||
|
config MTD_NAND_BCM_UMI_HWCS
|
||||||
|
bool "BCM UMI NAND Hardware CS"
|
||||||
|
depends on MTD_NAND_BCM_UMI
|
||||||
|
help
|
||||||
|
Enable the use of the BCM UMI block's internal CS using NAND.
|
||||||
|
This should only be used if you know the external NAND CS can toggle.
|
||||||
|
|
||||||
config MTD_NAND_DISKONCHIP
|
config MTD_NAND_DISKONCHIP
|
||||||
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
|
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
|
||||||
depends on EXPERIMENTAL
|
depends on EXPERIMENTAL
|
||||||
|
|
|
@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
|
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
|
||||||
obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
|
obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
|
||||||
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
|
obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
|
||||||
|
obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o
|
||||||
|
|
||||||
nand-objs := nand_base.o nand_bbt.o
|
nand-objs := nand_base.o nand_bbt.o
|
||||||
|
|
|
@ -372,15 +372,6 @@ static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob)
|
||||||
return __alauda_read_page(mtd, from, ignore_buf, oob);
|
return __alauda_read_page(mtd, from, ignore_buf, oob);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int popcount8(u8 c)
|
|
||||||
{
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
for ( ; c; c>>=1)
|
|
||||||
ret += c & 1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
|
static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
{
|
{
|
||||||
u8 oob[16];
|
u8 oob[16];
|
||||||
|
@ -391,7 +382,7 @@ static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* A block is marked bad if two or more bits are zero */
|
/* A block is marked bad if two or more bits are zero */
|
||||||
return popcount8(oob[5]) >= 7 ? 0 : 1;
|
return hweight8(oob[5]) >= 7 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
|
static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||||
|
|
|
@ -192,7 +192,6 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
|
||||||
{
|
{
|
||||||
struct nand_chip *nand_chip = mtd->priv;
|
struct nand_chip *nand_chip = mtd->priv;
|
||||||
struct atmel_nand_host *host = nand_chip->priv;
|
struct atmel_nand_host *host = nand_chip->priv;
|
||||||
uint32_t *eccpos = nand_chip->ecc.layout->eccpos;
|
|
||||||
unsigned int ecc_value;
|
unsigned int ecc_value;
|
||||||
|
|
||||||
/* get the first 2 ECC bytes */
|
/* get the first 2 ECC bytes */
|
||||||
|
@ -464,7 +463,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
||||||
if (host->board->det_pin) {
|
if (host->board->det_pin) {
|
||||||
if (gpio_get_value(host->board->det_pin)) {
|
if (gpio_get_value(host->board->det_pin)) {
|
||||||
printk(KERN_INFO "No SmartMedia card inserted.\n");
|
printk(KERN_INFO "No SmartMedia card inserted.\n");
|
||||||
res = ENXIO;
|
res = -ENXIO;
|
||||||
goto err_no_card;
|
goto err_no_card;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -535,7 +534,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
if ((!partitions) || (num_partitions == 0)) {
|
if ((!partitions) || (num_partitions == 0)) {
|
||||||
printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n");
|
printk(KERN_ERR "atmel_nand: No partitions defined, or unsupported device.\n");
|
||||||
res = ENXIO;
|
res = -ENXIO;
|
||||||
goto err_no_partitions;
|
goto err_no_partitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
213
drivers/mtd/nand/bcm_umi_bch.c
Normal file
213
drivers/mtd/nand/bcm_umi_bch.c
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Unless you and Broadcom execute a separate written software license
|
||||||
|
* agreement governing use of this software, this software is licensed to you
|
||||||
|
* under the terms of the GNU General Public License version 2, available at
|
||||||
|
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||||
|
*
|
||||||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||||||
|
* software in any way with any other Broadcom software provided under a
|
||||||
|
* license other than the GPL, without Broadcom's express prior written
|
||||||
|
* consent.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/* ---- Include Files ---------------------------------------------------- */
|
||||||
|
#include "nand_bcm_umi.h"
|
||||||
|
|
||||||
|
/* ---- External Variable Declarations ----------------------------------- */
|
||||||
|
/* ---- External Function Prototypes ------------------------------------- */
|
||||||
|
/* ---- Public Variables ------------------------------------------------- */
|
||||||
|
/* ---- Private Constants and Types -------------------------------------- */
|
||||||
|
|
||||||
|
/* ---- Private Function Prototypes -------------------------------------- */
|
||||||
|
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
||||||
|
struct nand_chip *chip, uint8_t *buf, int page);
|
||||||
|
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
|
||||||
|
struct nand_chip *chip, const uint8_t *buf);
|
||||||
|
|
||||||
|
/* ---- Private Variables ------------------------------------------------ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
** nand_hw_eccoob
|
||||||
|
** New oob placement block for use with hardware ecc generation.
|
||||||
|
*/
|
||||||
|
static struct nand_ecclayout nand_hw_eccoob_512 = {
|
||||||
|
/* Reserve 5 for BI indicator */
|
||||||
|
.oobfree = {
|
||||||
|
#if (NAND_ECC_NUM_BYTES > 3)
|
||||||
|
{.offset = 0, .length = 2}
|
||||||
|
#else
|
||||||
|
{.offset = 0, .length = 5},
|
||||||
|
{.offset = 6, .length = 7}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** We treat the OOB for a 2K page as if it were 4 512 byte oobs,
|
||||||
|
** except the BI is at byte 0.
|
||||||
|
*/
|
||||||
|
static struct nand_ecclayout nand_hw_eccoob_2048 = {
|
||||||
|
/* Reserve 0 as BI indicator */
|
||||||
|
.oobfree = {
|
||||||
|
#if (NAND_ECC_NUM_BYTES > 10)
|
||||||
|
{.offset = 1, .length = 2},
|
||||||
|
#elif (NAND_ECC_NUM_BYTES > 7)
|
||||||
|
{.offset = 1, .length = 5},
|
||||||
|
{.offset = 16, .length = 6},
|
||||||
|
{.offset = 32, .length = 6},
|
||||||
|
{.offset = 48, .length = 6}
|
||||||
|
#else
|
||||||
|
{.offset = 1, .length = 8},
|
||||||
|
{.offset = 16, .length = 9},
|
||||||
|
{.offset = 32, .length = 9},
|
||||||
|
{.offset = 48, .length = 9}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* We treat the OOB for a 4K page as if it were 8 512 byte oobs,
|
||||||
|
* except the BI is at byte 0. */
|
||||||
|
static struct nand_ecclayout nand_hw_eccoob_4096 = {
|
||||||
|
/* Reserve 0 as BI indicator */
|
||||||
|
.oobfree = {
|
||||||
|
#if (NAND_ECC_NUM_BYTES > 10)
|
||||||
|
{.offset = 1, .length = 2},
|
||||||
|
{.offset = 16, .length = 3},
|
||||||
|
{.offset = 32, .length = 3},
|
||||||
|
{.offset = 48, .length = 3},
|
||||||
|
{.offset = 64, .length = 3},
|
||||||
|
{.offset = 80, .length = 3},
|
||||||
|
{.offset = 96, .length = 3},
|
||||||
|
{.offset = 112, .length = 3}
|
||||||
|
#else
|
||||||
|
{.offset = 1, .length = 5},
|
||||||
|
{.offset = 16, .length = 6},
|
||||||
|
{.offset = 32, .length = 6},
|
||||||
|
{.offset = 48, .length = 6},
|
||||||
|
{.offset = 64, .length = 6},
|
||||||
|
{.offset = 80, .length = 6},
|
||||||
|
{.offset = 96, .length = 6},
|
||||||
|
{.offset = 112, .length = 6}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---- Private Functions ------------------------------------------------ */
|
||||||
|
/* ==== Public Functions ================================================= */
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function
|
||||||
|
* @mtd: mtd info structure
|
||||||
|
* @chip: nand chip info structure
|
||||||
|
* @buf: buffer to store read data
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
|
||||||
|
struct nand_chip *chip, uint8_t * buf,
|
||||||
|
int page)
|
||||||
|
{
|
||||||
|
int sectorIdx = 0;
|
||||||
|
int eccsize = chip->ecc.size;
|
||||||
|
int eccsteps = chip->ecc.steps;
|
||||||
|
uint8_t *datap = buf;
|
||||||
|
uint8_t eccCalc[NAND_ECC_NUM_BYTES];
|
||||||
|
int sectorOobSize = mtd->oobsize / eccsteps;
|
||||||
|
int stat;
|
||||||
|
|
||||||
|
for (sectorIdx = 0; sectorIdx < eccsteps;
|
||||||
|
sectorIdx++, datap += eccsize) {
|
||||||
|
if (sectorIdx > 0) {
|
||||||
|
/* Seek to page location within sector */
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
|
||||||
|
-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable hardware ECC before reading the buf */
|
||||||
|
nand_bcm_umi_bch_enable_read_hwecc();
|
||||||
|
|
||||||
|
/* Read in data */
|
||||||
|
bcm_umi_nand_read_buf(mtd, datap, eccsize);
|
||||||
|
|
||||||
|
/* Pause hardware ECC after reading the buf */
|
||||||
|
nand_bcm_umi_bch_pause_read_ecc_calc();
|
||||||
|
|
||||||
|
/* Read the OOB ECC */
|
||||||
|
chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
|
||||||
|
mtd->writesize + sectorIdx * sectorOobSize, -1);
|
||||||
|
nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
|
||||||
|
NAND_ECC_NUM_BYTES,
|
||||||
|
chip->oob_poi +
|
||||||
|
sectorIdx * sectorOobSize);
|
||||||
|
|
||||||
|
/* Correct any ECC detected errors */
|
||||||
|
stat =
|
||||||
|
nand_bcm_umi_bch_correct_page(datap, eccCalc,
|
||||||
|
NAND_ECC_NUM_BYTES);
|
||||||
|
|
||||||
|
/* Update Stats */
|
||||||
|
if (stat < 0) {
|
||||||
|
#if defined(NAND_BCM_UMI_DEBUG)
|
||||||
|
printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
|
||||||
|
__func__, sectorIdx);
|
||||||
|
printk(KERN_WARNING
|
||||||
|
"%s data %02x %02x %02x %02x "
|
||||||
|
"%02x %02x %02x %02x\n",
|
||||||
|
__func__, datap[0], datap[1], datap[2], datap[3],
|
||||||
|
datap[4], datap[5], datap[6], datap[7]);
|
||||||
|
printk(KERN_WARNING
|
||||||
|
"%s ecc %02x %02x %02x %02x "
|
||||||
|
"%02x %02x %02x %02x %02x %02x "
|
||||||
|
"%02x %02x %02x\n",
|
||||||
|
__func__, eccCalc[0], eccCalc[1], eccCalc[2],
|
||||||
|
eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
|
||||||
|
eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
|
||||||
|
eccCalc[11], eccCalc[12]);
|
||||||
|
BUG();
|
||||||
|
#endif
|
||||||
|
mtd->ecc_stats.failed++;
|
||||||
|
} else {
|
||||||
|
#if defined(NAND_BCM_UMI_DEBUG)
|
||||||
|
if (stat > 0) {
|
||||||
|
printk(KERN_INFO
|
||||||
|
"%s %d correctable_errors detected\n",
|
||||||
|
__func__, stat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
mtd->ecc_stats.corrected += stat;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
|
||||||
|
* @mtd: mtd info structure
|
||||||
|
* @chip: nand chip info structure
|
||||||
|
* @buf: data buffer
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
|
||||||
|
struct nand_chip *chip, const uint8_t *buf)
|
||||||
|
{
|
||||||
|
int sectorIdx = 0;
|
||||||
|
int eccsize = chip->ecc.size;
|
||||||
|
int eccsteps = chip->ecc.steps;
|
||||||
|
const uint8_t *datap = buf;
|
||||||
|
uint8_t *oobp = chip->oob_poi;
|
||||||
|
int sectorOobSize = mtd->oobsize / eccsteps;
|
||||||
|
|
||||||
|
for (sectorIdx = 0; sectorIdx < eccsteps;
|
||||||
|
sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
|
||||||
|
/* Enable hardware ECC before writing the buf */
|
||||||
|
nand_bcm_umi_bch_enable_write_hwecc();
|
||||||
|
bcm_umi_nand_write_buf(mtd, datap, eccsize);
|
||||||
|
nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
|
||||||
|
NAND_ECC_NUM_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||||
|
}
|
581
drivers/mtd/nand/bcm_umi_nand.c
Normal file
581
drivers/mtd/nand/bcm_umi_nand.c
Normal file
|
@ -0,0 +1,581 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Unless you and Broadcom execute a separate written software license
|
||||||
|
* agreement governing use of this software, this software is licensed to you
|
||||||
|
* under the terms of the GNU General Public License version 2, available at
|
||||||
|
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||||
|
*
|
||||||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||||||
|
* software in any way with any other Broadcom software provided under a
|
||||||
|
* license other than the GPL, without Broadcom's express prior written
|
||||||
|
* consent.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/* ---- Include Files ---------------------------------------------------- */
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/nand.h>
|
||||||
|
#include <linux/mtd/nand_ecc.h>
|
||||||
|
#include <linux/mtd/partitions.h>
|
||||||
|
|
||||||
|
#include <asm/mach-types.h>
|
||||||
|
#include <asm/system.h>
|
||||||
|
|
||||||
|
#include <mach/reg_nand.h>
|
||||||
|
#include <mach/reg_umi.h>
|
||||||
|
|
||||||
|
#include "nand_bcm_umi.h"
|
||||||
|
|
||||||
|
#include <mach/memory_settings.h>
|
||||||
|
|
||||||
|
#define USE_DMA 1
|
||||||
|
#include <mach/dma.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
|
||||||
|
/* ---- External Variable Declarations ----------------------------------- */
|
||||||
|
/* ---- External Function Prototypes ------------------------------------- */
|
||||||
|
/* ---- Public Variables ------------------------------------------------- */
|
||||||
|
/* ---- Private Constants and Types -------------------------------------- */
|
||||||
|
static const __devinitconst char gBanner[] = KERN_INFO \
|
||||||
|
"BCM UMI MTD NAND Driver: 1.00\n";
|
||||||
|
|
||||||
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
|
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
static uint8_t scan_ff_pattern[] = { 0xff };
|
||||||
|
|
||||||
|
static struct nand_bbt_descr largepage_bbt = {
|
||||||
|
.options = 0,
|
||||||
|
.offs = 0,
|
||||||
|
.len = 1,
|
||||||
|
.pattern = scan_ff_pattern
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Preallocate a buffer to avoid having to do this every dma operation.
|
||||||
|
** This is the size of the preallocated coherent DMA buffer.
|
||||||
|
*/
|
||||||
|
#if USE_DMA
|
||||||
|
#define DMA_MIN_BUFLEN 512
|
||||||
|
#define DMA_MAX_BUFLEN PAGE_SIZE
|
||||||
|
#define USE_DIRECT_IO(len) (((len) < DMA_MIN_BUFLEN) || \
|
||||||
|
((len) > DMA_MAX_BUFLEN))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The current NAND data space goes from 0x80001900 to 0x80001FFF,
|
||||||
|
* which is only 0x700 = 1792 bytes long. This is too small for 2K, 4K page
|
||||||
|
* size NAND flash. Need to break the DMA down to multiple 1Ks.
|
||||||
|
*
|
||||||
|
* Need to make sure REG_NAND_DATA_PADDR + DMA_MAX_LEN < 0x80002000
|
||||||
|
*/
|
||||||
|
#define DMA_MAX_LEN 1024
|
||||||
|
|
||||||
|
#else /* !USE_DMA */
|
||||||
|
#define DMA_MIN_BUFLEN 0
|
||||||
|
#define DMA_MAX_BUFLEN 0
|
||||||
|
#define USE_DIRECT_IO(len) 1
|
||||||
|
#endif
|
||||||
|
/* ---- Private Function Prototypes -------------------------------------- */
|
||||||
|
static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len);
|
||||||
|
static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf,
|
||||||
|
int len);
|
||||||
|
|
||||||
|
/* ---- Private Variables ------------------------------------------------ */
|
||||||
|
static struct mtd_info *board_mtd;
|
||||||
|
static void __iomem *bcm_umi_io_base;
|
||||||
|
static void *virtPtr;
|
||||||
|
static dma_addr_t physPtr;
|
||||||
|
static struct completion nand_comp;
|
||||||
|
|
||||||
|
/* ---- Private Functions ------------------------------------------------ */
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
#include "bcm_umi_bch.c"
|
||||||
|
#else
|
||||||
|
#include "bcm_umi_hamming.c"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_DMA
|
||||||
|
|
||||||
|
/* Handler called when the DMA finishes. */
|
||||||
|
static void nand_dma_handler(DMA_Device_t dev, int reason, void *userData)
|
||||||
|
{
|
||||||
|
complete(&nand_comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_dma_init(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = dma_set_device_handler(DMA_DEVICE_NAND_MEM_TO_MEM,
|
||||||
|
nand_dma_handler, NULL);
|
||||||
|
if (rc != 0) {
|
||||||
|
printk(KERN_ERR "dma_set_device_handler failed: %d\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtPtr =
|
||||||
|
dma_alloc_coherent(NULL, DMA_MAX_BUFLEN, &physPtr, GFP_KERNEL);
|
||||||
|
if (virtPtr == NULL) {
|
||||||
|
printk(KERN_ERR "NAND - Failed to allocate memory for DMA buffer\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nand_dma_term(void)
|
||||||
|
{
|
||||||
|
if (virtPtr != NULL)
|
||||||
|
dma_free_coherent(NULL, DMA_MAX_BUFLEN, virtPtr, physPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nand_dma_read(void *buf, int len)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
int tmp_len = 0;
|
||||||
|
int len_left = len;
|
||||||
|
DMA_Handle_t hndl;
|
||||||
|
|
||||||
|
if (virtPtr == NULL)
|
||||||
|
panic("nand_dma_read: virtPtr == NULL\n");
|
||||||
|
|
||||||
|
if ((void *)physPtr == NULL)
|
||||||
|
panic("nand_dma_read: physPtr == NULL\n");
|
||||||
|
|
||||||
|
hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM);
|
||||||
|
if (hndl < 0) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"nand_dma_read: unable to allocate dma channel: %d\n",
|
||||||
|
(int)hndl);
|
||||||
|
panic("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (len_left > 0) {
|
||||||
|
if (len_left > DMA_MAX_LEN) {
|
||||||
|
tmp_len = DMA_MAX_LEN;
|
||||||
|
len_left -= DMA_MAX_LEN;
|
||||||
|
} else {
|
||||||
|
tmp_len = len_left;
|
||||||
|
len_left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_completion(&nand_comp);
|
||||||
|
dma_transfer_mem_to_mem(hndl, REG_NAND_DATA_PADDR,
|
||||||
|
physPtr + offset, tmp_len);
|
||||||
|
wait_for_completion(&nand_comp);
|
||||||
|
|
||||||
|
offset += tmp_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_free_channel(hndl);
|
||||||
|
|
||||||
|
if (buf != NULL)
|
||||||
|
memcpy(buf, virtPtr, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nand_dma_write(const void *buf, int len)
|
||||||
|
{
|
||||||
|
int offset = 0;
|
||||||
|
int tmp_len = 0;
|
||||||
|
int len_left = len;
|
||||||
|
DMA_Handle_t hndl;
|
||||||
|
|
||||||
|
if (buf == NULL)
|
||||||
|
panic("nand_dma_write: buf == NULL\n");
|
||||||
|
|
||||||
|
if (virtPtr == NULL)
|
||||||
|
panic("nand_dma_write: virtPtr == NULL\n");
|
||||||
|
|
||||||
|
if ((void *)physPtr == NULL)
|
||||||
|
panic("nand_dma_write: physPtr == NULL\n");
|
||||||
|
|
||||||
|
memcpy(virtPtr, buf, len);
|
||||||
|
|
||||||
|
|
||||||
|
hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM);
|
||||||
|
if (hndl < 0) {
|
||||||
|
printk(KERN_ERR
|
||||||
|
"nand_dma_write: unable to allocate dma channel: %d\n",
|
||||||
|
(int)hndl);
|
||||||
|
panic("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (len_left > 0) {
|
||||||
|
if (len_left > DMA_MAX_LEN) {
|
||||||
|
tmp_len = DMA_MAX_LEN;
|
||||||
|
len_left -= DMA_MAX_LEN;
|
||||||
|
} else {
|
||||||
|
tmp_len = len_left;
|
||||||
|
len_left = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_completion(&nand_comp);
|
||||||
|
dma_transfer_mem_to_mem(hndl, physPtr + offset,
|
||||||
|
REG_NAND_DATA_PADDR, tmp_len);
|
||||||
|
wait_for_completion(&nand_comp);
|
||||||
|
|
||||||
|
offset += tmp_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_free_channel(hndl);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int nand_dev_ready(struct mtd_info *mtd)
|
||||||
|
{
|
||||||
|
return nand_bcm_umi_dev_ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
*
|
||||||
|
* bcm_umi_nand_inithw
|
||||||
|
*
|
||||||
|
* This routine does the necessary hardware (board-specific)
|
||||||
|
* initializations. This includes setting up the timings, etc.
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
int bcm_umi_nand_inithw(void)
|
||||||
|
{
|
||||||
|
/* Configure nand timing parameters */
|
||||||
|
REG_UMI_NAND_TCR &= ~0x7ffff;
|
||||||
|
REG_UMI_NAND_TCR |= HW_CFG_NAND_TCR;
|
||||||
|
|
||||||
|
#if !defined(CONFIG_MTD_NAND_BCM_UMI_HWCS)
|
||||||
|
/* enable software control of CS */
|
||||||
|
REG_UMI_NAND_TCR |= REG_UMI_NAND_TCR_CS_SWCTRL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* keep NAND chip select asserted */
|
||||||
|
REG_UMI_NAND_RCSR |= REG_UMI_NAND_RCSR_CS_ASSERTED;
|
||||||
|
|
||||||
|
REG_UMI_NAND_TCR &= ~REG_UMI_NAND_TCR_WORD16;
|
||||||
|
/* enable writes to flash */
|
||||||
|
REG_UMI_MMD_ICR |= REG_UMI_MMD_ICR_FLASH_WP;
|
||||||
|
|
||||||
|
writel(NAND_CMD_RESET, bcm_umi_io_base + REG_NAND_CMD_OFFSET);
|
||||||
|
nand_bcm_umi_wait_till_ready();
|
||||||
|
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
nand_bcm_umi_bch_config_ecc(NAND_ECC_NUM_BYTES);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to turn latch the proper register for access. */
|
||||||
|
static void bcm_umi_nand_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||||
|
unsigned int ctrl)
|
||||||
|
{
|
||||||
|
/* send command to hardware */
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
if (ctrl & NAND_CTRL_CHANGE) {
|
||||||
|
if (ctrl & NAND_CLE) {
|
||||||
|
chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_CMD_OFFSET;
|
||||||
|
goto CMD;
|
||||||
|
}
|
||||||
|
if (ctrl & NAND_ALE) {
|
||||||
|
chip->IO_ADDR_W =
|
||||||
|
bcm_umi_io_base + REG_NAND_ADDR_OFFSET;
|
||||||
|
goto CMD;
|
||||||
|
}
|
||||||
|
chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMD:
|
||||||
|
/* Send command to chip directly */
|
||||||
|
if (cmd != NAND_CMD_NONE)
|
||||||
|
writeb(cmd, chip->IO_ADDR_W);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf,
|
||||||
|
int len)
|
||||||
|
{
|
||||||
|
if (USE_DIRECT_IO(len)) {
|
||||||
|
/* Do it the old way if the buffer is small or too large.
|
||||||
|
* Probably quicker than starting and checking dma. */
|
||||||
|
int i;
|
||||||
|
struct nand_chip *this = mtd->priv;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
writeb(buf[i], this->IO_ADDR_W);
|
||||||
|
}
|
||||||
|
#if USE_DMA
|
||||||
|
else
|
||||||
|
nand_dma_write(buf, len);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
|
||||||
|
{
|
||||||
|
if (USE_DIRECT_IO(len)) {
|
||||||
|
int i;
|
||||||
|
struct nand_chip *this = mtd->priv;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
buf[i] = readb(this->IO_ADDR_R);
|
||||||
|
}
|
||||||
|
#if USE_DMA
|
||||||
|
else
|
||||||
|
nand_dma_read(buf, len);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t readbackbuf[NAND_MAX_PAGESIZE];
|
||||||
|
static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf,
|
||||||
|
int len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Try to readback page with ECC correction. This is necessary
|
||||||
|
* for MLC parts which may have permanently stuck bits.
|
||||||
|
*/
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return -EFAULT;
|
||||||
|
else {
|
||||||
|
if (memcmp(readbackbuf, buf, len) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct nand_chip *this;
|
||||||
|
struct resource *r;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
printk(gBanner);
|
||||||
|
|
||||||
|
/* Allocate memory for MTD device structure and private data */
|
||||||
|
board_mtd =
|
||||||
|
kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!board_mtd) {
|
||||||
|
printk(KERN_WARNING
|
||||||
|
"Unable to allocate NAND MTD device structure.\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
|
||||||
|
if (!r)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
|
/* map physical adress */
|
||||||
|
bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1);
|
||||||
|
|
||||||
|
if (!bcm_umi_io_base) {
|
||||||
|
printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n");
|
||||||
|
kfree(board_mtd);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get pointer to private data */
|
||||||
|
this = (struct nand_chip *)(&board_mtd[1]);
|
||||||
|
|
||||||
|
/* Initialize structures */
|
||||||
|
memset((char *)board_mtd, 0, sizeof(struct mtd_info));
|
||||||
|
memset((char *)this, 0, sizeof(struct nand_chip));
|
||||||
|
|
||||||
|
/* Link the private data with the MTD structure */
|
||||||
|
board_mtd->priv = this;
|
||||||
|
|
||||||
|
/* Initialize the NAND hardware. */
|
||||||
|
if (bcm_umi_nand_inithw() < 0) {
|
||||||
|
printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n");
|
||||||
|
iounmap(bcm_umi_io_base);
|
||||||
|
kfree(board_mtd);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set address of NAND IO lines */
|
||||||
|
this->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
|
||||||
|
this->IO_ADDR_R = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
|
||||||
|
|
||||||
|
/* Set command delay time, see datasheet for correct value */
|
||||||
|
this->chip_delay = 0;
|
||||||
|
/* Assign the device ready function, if available */
|
||||||
|
this->dev_ready = nand_dev_ready;
|
||||||
|
this->options = 0;
|
||||||
|
|
||||||
|
this->write_buf = bcm_umi_nand_write_buf;
|
||||||
|
this->read_buf = bcm_umi_nand_read_buf;
|
||||||
|
this->verify_buf = bcm_umi_nand_verify_buf;
|
||||||
|
|
||||||
|
this->cmd_ctrl = bcm_umi_nand_hwcontrol;
|
||||||
|
this->ecc.mode = NAND_ECC_HW;
|
||||||
|
this->ecc.size = 512;
|
||||||
|
this->ecc.bytes = NAND_ECC_NUM_BYTES;
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
this->ecc.read_page = bcm_umi_bch_read_page_hwecc;
|
||||||
|
this->ecc.write_page = bcm_umi_bch_write_page_hwecc;
|
||||||
|
#else
|
||||||
|
this->ecc.correct = nand_correct_data512;
|
||||||
|
this->ecc.calculate = bcm_umi_hamming_get_hw_ecc;
|
||||||
|
this->ecc.hwctl = bcm_umi_hamming_enable_hwecc;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if USE_DMA
|
||||||
|
err = nand_dma_init();
|
||||||
|
if (err != 0)
|
||||||
|
return err;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Figure out the size of the device that we have.
|
||||||
|
* We need to do this to figure out which ECC
|
||||||
|
* layout we'll be using.
|
||||||
|
*/
|
||||||
|
|
||||||
|
err = nand_scan_ident(board_mtd, 1);
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_ERR "nand_scan failed: %d\n", err);
|
||||||
|
iounmap(bcm_umi_io_base);
|
||||||
|
kfree(board_mtd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now that we know the nand size, we can setup the ECC layout */
|
||||||
|
|
||||||
|
switch (board_mtd->writesize) { /* writesize is the pagesize */
|
||||||
|
case 4096:
|
||||||
|
this->ecc.layout = &nand_hw_eccoob_4096;
|
||||||
|
break;
|
||||||
|
case 2048:
|
||||||
|
this->ecc.layout = &nand_hw_eccoob_2048;
|
||||||
|
break;
|
||||||
|
case 512:
|
||||||
|
this->ecc.layout = &nand_hw_eccoob_512;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n",
|
||||||
|
board_mtd->writesize);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
if (board_mtd->writesize > 512) {
|
||||||
|
if (this->options & NAND_USE_FLASH_BBT)
|
||||||
|
largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
|
||||||
|
this->badblock_pattern = &largepage_bbt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Now finish off the scan, now that ecc.layout has been initialized. */
|
||||||
|
|
||||||
|
err = nand_scan_tail(board_mtd);
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_ERR "nand_scan failed: %d\n", err);
|
||||||
|
iounmap(bcm_umi_io_base);
|
||||||
|
kfree(board_mtd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register the partitions */
|
||||||
|
{
|
||||||
|
int nr_partitions;
|
||||||
|
struct mtd_partition *partition_info;
|
||||||
|
|
||||||
|
board_mtd->name = "bcm_umi-nand";
|
||||||
|
nr_partitions =
|
||||||
|
parse_mtd_partitions(board_mtd, part_probes,
|
||||||
|
&partition_info, 0);
|
||||||
|
|
||||||
|
if (nr_partitions <= 0) {
|
||||||
|
printk(KERN_ERR "BCM UMI NAND: Too few partitions - %d\n",
|
||||||
|
nr_partitions);
|
||||||
|
iounmap(bcm_umi_io_base);
|
||||||
|
kfree(board_mtd);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
add_mtd_partitions(board_mtd, partition_info, nr_partitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return happy */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_umi_nand_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
#if USE_DMA
|
||||||
|
nand_dma_term();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Release resources, unregister device */
|
||||||
|
nand_release(board_mtd);
|
||||||
|
|
||||||
|
/* unmap physical adress */
|
||||||
|
iounmap(bcm_umi_io_base);
|
||||||
|
|
||||||
|
/* Free the MTD device structure */
|
||||||
|
kfree(board_mtd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int bcm_umi_nand_suspend(struct platform_device *pdev,
|
||||||
|
pm_message_t state)
|
||||||
|
{
|
||||||
|
printk(KERN_ERR "MTD NAND suspend is being called\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bcm_umi_nand_resume(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
printk(KERN_ERR "MTD NAND resume is being called\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define bcm_umi_nand_suspend NULL
|
||||||
|
#define bcm_umi_nand_resume NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct platform_driver nand_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "bcm-nand",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = bcm_umi_nand_probe,
|
||||||
|
.remove = bcm_umi_nand_remove,
|
||||||
|
.suspend = bcm_umi_nand_suspend,
|
||||||
|
.resume = bcm_umi_nand_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init nand_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&nand_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit nand_exit(void)
|
||||||
|
{
|
||||||
|
platform_driver_unregister(&nand_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(nand_init);
|
||||||
|
module_exit(nand_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Broadcom");
|
||||||
|
MODULE_DESCRIPTION("BCM UMI MTD NAND driver");
|
|
@ -591,6 +591,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* options such as NAND_USE_FLASH_BBT or 16-bit widths */
|
/* options such as NAND_USE_FLASH_BBT or 16-bit widths */
|
||||||
info->chip.options = pdata->options;
|
info->chip.options = pdata->options;
|
||||||
|
info->chip.bbt_td = pdata->bbt_td;
|
||||||
|
info->chip.bbt_md = pdata->bbt_md;
|
||||||
|
|
||||||
info->ioaddr = (uint32_t __force) vaddr;
|
info->ioaddr = (uint32_t __force) vaddr;
|
||||||
|
|
||||||
|
@ -599,7 +601,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||||
info->mask_chipsel = pdata->mask_chipsel;
|
info->mask_chipsel = pdata->mask_chipsel;
|
||||||
|
|
||||||
/* use nandboot-capable ALE/CLE masks by default */
|
/* use nandboot-capable ALE/CLE masks by default */
|
||||||
info->mask_ale = pdata->mask_cle ? : MASK_ALE;
|
info->mask_ale = pdata->mask_ale ? : MASK_ALE;
|
||||||
info->mask_cle = pdata->mask_cle ? : MASK_CLE;
|
info->mask_cle = pdata->mask_cle ? : MASK_CLE;
|
||||||
|
|
||||||
/* Set address of hardware control function */
|
/* Set address of hardware control function */
|
||||||
|
|
|
@ -128,7 +128,7 @@ static int excite_nand_devready(struct mtd_info *mtd)
|
||||||
* The binding to the mtd and all allocated
|
* The binding to the mtd and all allocated
|
||||||
* resources are released.
|
* resources are released.
|
||||||
*/
|
*/
|
||||||
static int __exit excite_nand_remove(struct platform_device *dev)
|
static int __devexit excite_nand_remove(struct platform_device *dev)
|
||||||
{
|
{
|
||||||
struct excite_nand_drvdata * const this = platform_get_drvdata(dev);
|
struct excite_nand_drvdata * const this = platform_get_drvdata(dev);
|
||||||
|
|
||||||
|
|
|
@ -237,12 +237,15 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
|
||||||
|
|
||||||
ctrl->use_mdr = 0;
|
ctrl->use_mdr = 0;
|
||||||
|
|
||||||
dev_vdbg(ctrl->dev,
|
if (ctrl->status != LTESR_CC) {
|
||||||
"fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n",
|
dev_info(ctrl->dev,
|
||||||
ctrl->status, ctrl->mdr, in_be32(&lbc->fmr));
|
"command failed: fir %x fcr %x status %x mdr %x\n",
|
||||||
|
in_be32(&lbc->fir), in_be32(&lbc->fcr),
|
||||||
|
ctrl->status, ctrl->mdr);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
/* returns 0 on success otherwise non-zero) */
|
return 0;
|
||||||
return ctrl->status == LTESR_CC ? 0 : -EIO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
|
static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
|
||||||
|
@ -253,17 +256,17 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
|
||||||
|
|
||||||
if (priv->page_size) {
|
if (priv->page_size) {
|
||||||
out_be32(&lbc->fir,
|
out_be32(&lbc->fir,
|
||||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||||
(FIR_OP_CW1 << FIR_OP3_SHIFT) |
|
(FIR_OP_CM1 << FIR_OP3_SHIFT) |
|
||||||
(FIR_OP_RBW << FIR_OP4_SHIFT));
|
(FIR_OP_RBW << FIR_OP4_SHIFT));
|
||||||
|
|
||||||
out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
|
out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
|
||||||
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
|
(NAND_CMD_READSTART << FCR_CMD1_SHIFT));
|
||||||
} else {
|
} else {
|
||||||
out_be32(&lbc->fir,
|
out_be32(&lbc->fir,
|
||||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||||
(FIR_OP_RBW << FIR_OP3_SHIFT));
|
(FIR_OP_RBW << FIR_OP3_SHIFT));
|
||||||
|
@ -332,7 +335,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||||
case NAND_CMD_READID:
|
case NAND_CMD_READID:
|
||||||
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
|
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
|
||||||
|
|
||||||
out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||||
(FIR_OP_UA << FIR_OP1_SHIFT) |
|
(FIR_OP_UA << FIR_OP1_SHIFT) |
|
||||||
(FIR_OP_RBW << FIR_OP2_SHIFT));
|
(FIR_OP_RBW << FIR_OP2_SHIFT));
|
||||||
out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
|
out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
|
||||||
|
@ -359,16 +362,20 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||||
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
|
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
|
||||||
|
|
||||||
out_be32(&lbc->fir,
|
out_be32(&lbc->fir,
|
||||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||||
(FIR_OP_PA << FIR_OP1_SHIFT) |
|
(FIR_OP_PA << FIR_OP1_SHIFT) |
|
||||||
(FIR_OP_CM1 << FIR_OP2_SHIFT));
|
(FIR_OP_CM2 << FIR_OP2_SHIFT) |
|
||||||
|
(FIR_OP_CW1 << FIR_OP3_SHIFT) |
|
||||||
|
(FIR_OP_RS << FIR_OP4_SHIFT));
|
||||||
|
|
||||||
out_be32(&lbc->fcr,
|
out_be32(&lbc->fcr,
|
||||||
(NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
|
(NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
|
||||||
(NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));
|
(NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
|
||||||
|
(NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
|
||||||
|
|
||||||
out_be32(&lbc->fbcr, 0);
|
out_be32(&lbc->fbcr, 0);
|
||||||
ctrl->read_bytes = 0;
|
ctrl->read_bytes = 0;
|
||||||
|
ctrl->use_mdr = 1;
|
||||||
|
|
||||||
fsl_elbc_run_command(mtd);
|
fsl_elbc_run_command(mtd);
|
||||||
return;
|
return;
|
||||||
|
@ -383,40 +390,41 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
|
||||||
|
|
||||||
ctrl->column = column;
|
ctrl->column = column;
|
||||||
ctrl->oob = 0;
|
ctrl->oob = 0;
|
||||||
|
ctrl->use_mdr = 1;
|
||||||
|
|
||||||
|
fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
|
||||||
|
(NAND_CMD_SEQIN << FCR_CMD2_SHIFT) |
|
||||||
|
(NAND_CMD_PAGEPROG << FCR_CMD3_SHIFT);
|
||||||
|
|
||||||
if (priv->page_size) {
|
if (priv->page_size) {
|
||||||
fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) |
|
|
||||||
(NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);
|
|
||||||
|
|
||||||
out_be32(&lbc->fir,
|
out_be32(&lbc->fir,
|
||||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
(FIR_OP_CM2 << FIR_OP0_SHIFT) |
|
||||||
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
(FIR_OP_CA << FIR_OP1_SHIFT) |
|
||||||
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
(FIR_OP_PA << FIR_OP2_SHIFT) |
|
||||||
(FIR_OP_WB << FIR_OP3_SHIFT) |
|
(FIR_OP_WB << FIR_OP3_SHIFT) |
|
||||||
(FIR_OP_CW1 << FIR_OP4_SHIFT));
|
(FIR_OP_CM3 << FIR_OP4_SHIFT) |
|
||||||
|
(FIR_OP_CW1 << FIR_OP5_SHIFT) |
|
||||||
|
(FIR_OP_RS << FIR_OP6_SHIFT));
|
||||||
} else {
|
} else {
|
||||||
fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |
|
|
||||||
(NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
|
|
||||||
|
|
||||||
out_be32(&lbc->fir,
|
out_be32(&lbc->fir,
|
||||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
(FIR_OP_CM0 << FIR_OP0_SHIFT) |
|
||||||
(FIR_OP_CM2 << FIR_OP1_SHIFT) |
|
(FIR_OP_CM2 << FIR_OP1_SHIFT) |
|
||||||
(FIR_OP_CA << FIR_OP2_SHIFT) |
|
(FIR_OP_CA << FIR_OP2_SHIFT) |
|
||||||
(FIR_OP_PA << FIR_OP3_SHIFT) |
|
(FIR_OP_PA << FIR_OP3_SHIFT) |
|
||||||
(FIR_OP_WB << FIR_OP4_SHIFT) |
|
(FIR_OP_WB << FIR_OP4_SHIFT) |
|
||||||
(FIR_OP_CW1 << FIR_OP5_SHIFT));
|
(FIR_OP_CM3 << FIR_OP5_SHIFT) |
|
||||||
|
(FIR_OP_CW1 << FIR_OP6_SHIFT) |
|
||||||
|
(FIR_OP_RS << FIR_OP7_SHIFT));
|
||||||
|
|
||||||
if (column >= mtd->writesize) {
|
if (column >= mtd->writesize) {
|
||||||
/* OOB area --> READOOB */
|
/* OOB area --> READOOB */
|
||||||
column -= mtd->writesize;
|
column -= mtd->writesize;
|
||||||
fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
|
fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
|
||||||
ctrl->oob = 1;
|
ctrl->oob = 1;
|
||||||
} else if (column < 256) {
|
} else {
|
||||||
|
WARN_ON(column != 0);
|
||||||
/* First 256 bytes --> READ0 */
|
/* First 256 bytes --> READ0 */
|
||||||
fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
|
fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
|
||||||
} else {
|
|
||||||
/* Second 256 bytes --> READ1 */
|
|
||||||
fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,22 +636,6 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
{
|
{
|
||||||
struct fsl_elbc_mtd *priv = chip->priv;
|
struct fsl_elbc_mtd *priv = chip->priv;
|
||||||
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
struct fsl_elbc_ctrl *ctrl = priv->ctrl;
|
||||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
|
||||||
|
|
||||||
if (ctrl->status != LTESR_CC)
|
|
||||||
return NAND_STATUS_FAIL;
|
|
||||||
|
|
||||||
/* Use READ_STATUS command, but wait for the device to be ready */
|
|
||||||
ctrl->use_mdr = 0;
|
|
||||||
out_be32(&lbc->fir,
|
|
||||||
(FIR_OP_CW0 << FIR_OP0_SHIFT) |
|
|
||||||
(FIR_OP_RBW << FIR_OP1_SHIFT));
|
|
||||||
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;
|
|
||||||
|
|
||||||
fsl_elbc_run_command(mtd);
|
|
||||||
|
|
||||||
if (ctrl->status != LTESR_CC)
|
if (ctrl->status != LTESR_CC)
|
||||||
return NAND_STATUS_FAIL;
|
return NAND_STATUS_FAIL;
|
||||||
|
@ -651,8 +643,7 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
/* The chip always seems to report that it is
|
/* The chip always seems to report that it is
|
||||||
* write-protected, even when it is not.
|
* write-protected, even when it is not.
|
||||||
*/
|
*/
|
||||||
setbits8(ctrl->addr, NAND_STATUS_WP);
|
return (ctrl->mdr & 0xff) | NAND_STATUS_WP;
|
||||||
return fsl_elbc_read_byte(mtd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
|
||||||
|
@ -946,6 +937,13 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
|
||||||
{
|
{
|
||||||
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
|
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 */
|
/* clear event registers */
|
||||||
setbits32(&lbc->ltesr, LTESR_NAND_MASK);
|
setbits32(&lbc->ltesr, LTESR_NAND_MASK);
|
||||||
out_be32(&lbc->lteatr, 0);
|
out_be32(&lbc->lteatr, 0);
|
||||||
|
|
|
@ -112,7 +112,7 @@ static void fun_select_chip(struct mtd_info *mtd, int mchip_nr)
|
||||||
|
|
||||||
if (mchip_nr == -1) {
|
if (mchip_nr == -1) {
|
||||||
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
|
||||||
} else if (mchip_nr >= 0) {
|
} else if (mchip_nr >= 0 && mchip_nr < NAND_MAX_CHIPS) {
|
||||||
fun->mchip_number = mchip_nr;
|
fun->mchip_number = mchip_nr;
|
||||||
chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
|
chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
|
||||||
chip->IO_ADDR_W = chip->IO_ADDR_R;
|
chip->IO_ADDR_W = chip->IO_ADDR_R;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -428,6 +428,28 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
|
||||||
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
return nand_isbad_bbt(mtd, ofs, allowbbt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @timeo: Timeout
|
||||||
|
*
|
||||||
|
* Helper function for nand_wait_ready used when needing to wait in interrupt
|
||||||
|
* context.
|
||||||
|
*/
|
||||||
|
static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Wait for the device to get ready */
|
||||||
|
for (i = 0; i < timeo; i++) {
|
||||||
|
if (chip->dev_ready(mtd))
|
||||||
|
break;
|
||||||
|
touch_softlockup_watchdog();
|
||||||
|
mdelay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wait for the ready pin, after a command
|
* Wait for the ready pin, after a command
|
||||||
* The timeout is catched later.
|
* The timeout is catched later.
|
||||||
|
@ -437,6 +459,10 @@ void nand_wait_ready(struct mtd_info *mtd)
|
||||||
struct nand_chip *chip = mtd->priv;
|
struct nand_chip *chip = mtd->priv;
|
||||||
unsigned long timeo = jiffies + 2;
|
unsigned long timeo = jiffies + 2;
|
||||||
|
|
||||||
|
/* 400ms timeout */
|
||||||
|
if (in_interrupt() || oops_in_progress)
|
||||||
|
return panic_nand_wait_ready(mtd, 400);
|
||||||
|
|
||||||
led_trigger_event(nand_led_trigger, LED_FULL);
|
led_trigger_event(nand_led_trigger, LED_FULL);
|
||||||
/* wait until command is processed or timeout occures */
|
/* wait until command is processed or timeout occures */
|
||||||
do {
|
do {
|
||||||
|
@ -671,6 +697,22 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
||||||
nand_wait_ready(mtd);
|
nand_wait_ready(mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* panic_nand_get_device - [GENERIC] Get chip for selected access
|
||||||
|
* @chip: the nand chip descriptor
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @new_state: the state which is requested
|
||||||
|
*
|
||||||
|
* Used when in panic, no locks are taken.
|
||||||
|
*/
|
||||||
|
static void panic_nand_get_device(struct nand_chip *chip,
|
||||||
|
struct mtd_info *mtd, int new_state)
|
||||||
|
{
|
||||||
|
/* Hardware controller shared among independend devices */
|
||||||
|
chip->controller->active = chip;
|
||||||
|
chip->state = new_state;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_get_device - [GENERIC] Get chip for selected access
|
* nand_get_device - [GENERIC] Get chip for selected access
|
||||||
* @chip: the nand chip descriptor
|
* @chip: the nand chip descriptor
|
||||||
|
@ -698,8 +740,14 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (new_state == FL_PM_SUSPENDED) {
|
if (new_state == FL_PM_SUSPENDED) {
|
||||||
spin_unlock(lock);
|
if (chip->controller->active->state == FL_PM_SUSPENDED) {
|
||||||
return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
|
chip->state = FL_PM_SUSPENDED;
|
||||||
|
spin_unlock(lock);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
spin_unlock(lock);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||||
add_wait_queue(wq, &wait);
|
add_wait_queue(wq, &wait);
|
||||||
|
@ -709,6 +757,32 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* panic_nand_wait - [GENERIC] wait until the command is done
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @chip: NAND chip structure
|
||||||
|
* @timeo: Timeout
|
||||||
|
*
|
||||||
|
* Wait for command done. This is a helper function for nand_wait used when
|
||||||
|
* we are in interrupt context. May happen when in panic and trying to write
|
||||||
|
* an oops trough mtdoops.
|
||||||
|
*/
|
||||||
|
static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
|
||||||
|
unsigned long timeo)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < timeo; i++) {
|
||||||
|
if (chip->dev_ready) {
|
||||||
|
if (chip->dev_ready(mtd))
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mdelay(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_wait - [DEFAULT] wait until the command is done
|
* nand_wait - [DEFAULT] wait until the command is done
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
|
@ -740,15 +814,19 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
||||||
else
|
else
|
||||||
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
||||||
|
|
||||||
while (time_before(jiffies, timeo)) {
|
if (in_interrupt() || oops_in_progress)
|
||||||
if (chip->dev_ready) {
|
panic_nand_wait(mtd, chip, timeo);
|
||||||
if (chip->dev_ready(mtd))
|
else {
|
||||||
break;
|
while (time_before(jiffies, timeo)) {
|
||||||
} else {
|
if (chip->dev_ready) {
|
||||||
if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
if (chip->dev_ready(mtd))
|
||||||
break;
|
break;
|
||||||
|
} else {
|
||||||
|
if (chip->read_byte(mtd) & NAND_STATUS_READY)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cond_resched();
|
||||||
}
|
}
|
||||||
cond_resched();
|
|
||||||
}
|
}
|
||||||
led_trigger_event(nand_led_trigger, LED_OFF);
|
led_trigger_event(nand_led_trigger, LED_OFF);
|
||||||
|
|
||||||
|
@ -1948,6 +2026,45 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* panic_nand_write - [MTD Interface] NAND write with ECC
|
||||||
|
* @mtd: MTD device structure
|
||||||
|
* @to: offset to write to
|
||||||
|
* @len: number of bytes to write
|
||||||
|
* @retlen: pointer to variable to store the number of written bytes
|
||||||
|
* @buf: the data to write
|
||||||
|
*
|
||||||
|
* NAND write with ECC. Used when performing writes in interrupt context, this
|
||||||
|
* may for example be called by mtdoops when writing an oops while in panic.
|
||||||
|
*/
|
||||||
|
static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||||
|
size_t *retlen, const uint8_t *buf)
|
||||||
|
{
|
||||||
|
struct nand_chip *chip = mtd->priv;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Do not allow reads past end of device */
|
||||||
|
if ((to + len) > mtd->size)
|
||||||
|
return -EINVAL;
|
||||||
|
if (!len)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Wait for the device to get ready. */
|
||||||
|
panic_nand_wait(mtd, chip, 400);
|
||||||
|
|
||||||
|
/* Grab the device. */
|
||||||
|
panic_nand_get_device(chip, mtd, FL_WRITING);
|
||||||
|
|
||||||
|
chip->ops.len = len;
|
||||||
|
chip->ops.datbuf = (uint8_t *)buf;
|
||||||
|
chip->ops.oobbuf = NULL;
|
||||||
|
|
||||||
|
ret = nand_do_write_ops(mtd, to, &chip->ops);
|
||||||
|
|
||||||
|
*retlen = chip->ops.retlen;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_write - [MTD Interface] NAND write with ECC
|
* nand_write - [MTD Interface] NAND write with ECC
|
||||||
* @mtd: MTD device structure
|
* @mtd: MTD device structure
|
||||||
|
@ -2645,7 +2762,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
|
||||||
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
|
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
|
||||||
|
|
||||||
if (IS_ERR(type)) {
|
if (IS_ERR(type)) {
|
||||||
printk(KERN_WARNING "No NAND device found!!!\n");
|
if (!(chip->options & NAND_SCAN_SILENT_NODEV))
|
||||||
|
printk(KERN_WARNING "No NAND device found.\n");
|
||||||
chip->select_chip(mtd, -1);
|
chip->select_chip(mtd, -1);
|
||||||
return PTR_ERR(type);
|
return PTR_ERR(type);
|
||||||
}
|
}
|
||||||
|
@ -2877,6 +2995,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
||||||
mtd->unpoint = NULL;
|
mtd->unpoint = NULL;
|
||||||
mtd->read = nand_read;
|
mtd->read = nand_read;
|
||||||
mtd->write = nand_write;
|
mtd->write = nand_write;
|
||||||
|
mtd->panic_write = panic_nand_write;
|
||||||
mtd->read_oob = nand_read_oob;
|
mtd->read_oob = nand_read_oob;
|
||||||
mtd->write_oob = nand_write_oob;
|
mtd->write_oob = nand_write_oob;
|
||||||
mtd->sync = nand_sync;
|
mtd->sync = nand_sync;
|
||||||
|
|
149
drivers/mtd/nand/nand_bcm_umi.c
Normal file
149
drivers/mtd/nand/nand_bcm_umi.c
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Unless you and Broadcom execute a separate written software license
|
||||||
|
* agreement governing use of this software, this software is licensed to you
|
||||||
|
* under the terms of the GNU General Public License version 2, available at
|
||||||
|
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||||
|
*
|
||||||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||||||
|
* software in any way with any other Broadcom software provided under a
|
||||||
|
* license other than the GPL, without Broadcom's express prior written
|
||||||
|
* consent.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/* ---- Include Files ---------------------------------------------------- */
|
||||||
|
#include <mach/reg_umi.h>
|
||||||
|
#include "nand_bcm_umi.h"
|
||||||
|
#ifdef BOOT0_BUILD
|
||||||
|
#include <uart.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ---- External Variable Declarations ----------------------------------- */
|
||||||
|
/* ---- External Function Prototypes ------------------------------------- */
|
||||||
|
/* ---- Public Variables ------------------------------------------------- */
|
||||||
|
/* ---- Private Constants and Types -------------------------------------- */
|
||||||
|
/* ---- Private Function Prototypes -------------------------------------- */
|
||||||
|
/* ---- Private Variables ------------------------------------------------ */
|
||||||
|
/* ---- Private Functions ------------------------------------------------ */
|
||||||
|
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
/****************************************************************************
|
||||||
|
* nand_bch_ecc_flip_bit - Routine to flip an errored bit
|
||||||
|
*
|
||||||
|
* PURPOSE:
|
||||||
|
* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the
|
||||||
|
* errored bit specified
|
||||||
|
*
|
||||||
|
* PARAMETERS:
|
||||||
|
* datap - Container that holds the 512 byte data
|
||||||
|
* errorLocation - Location of the bit that needs to be flipped
|
||||||
|
*
|
||||||
|
* RETURNS:
|
||||||
|
* None
|
||||||
|
****************************************************************************/
|
||||||
|
static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation)
|
||||||
|
{
|
||||||
|
int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0;
|
||||||
|
int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3;
|
||||||
|
int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5;
|
||||||
|
|
||||||
|
uint8_t errorByte = 0;
|
||||||
|
uint8_t byteMask = 1 << locWithinAByte;
|
||||||
|
|
||||||
|
/* BCH uses big endian, need to change the location
|
||||||
|
* bits to little endian */
|
||||||
|
locWithinAWord = 3 - locWithinAWord;
|
||||||
|
|
||||||
|
errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord];
|
||||||
|
|
||||||
|
#ifdef BOOT0_BUILD
|
||||||
|
puthexs("\nECC Correct Offset: ",
|
||||||
|
locWithinAPage * sizeof(uint32_t) + locWithinAWord);
|
||||||
|
puthexs(" errorByte:", errorByte);
|
||||||
|
puthex8(" Bit: ", locWithinAByte);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (errorByte & byteMask) {
|
||||||
|
/* bit needs to be cleared */
|
||||||
|
errorByte &= ~byteMask;
|
||||||
|
} else {
|
||||||
|
/* bit needs to be set */
|
||||||
|
errorByte |= byteMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write back the value with the fixed bit */
|
||||||
|
datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* nand_correct_page_bch - Routine to correct bit errors when reading NAND
|
||||||
|
*
|
||||||
|
* PURPOSE:
|
||||||
|
* This routine reads the BCH registers to determine if there are any bit
|
||||||
|
* errors during the read of the last 512 bytes of data + ECC bytes. If
|
||||||
|
* errors exists, the routine fixes it.
|
||||||
|
*
|
||||||
|
* PARAMETERS:
|
||||||
|
* datap - Container that holds the 512 byte data
|
||||||
|
*
|
||||||
|
* RETURNS:
|
||||||
|
* 0 or greater = Number of errors corrected
|
||||||
|
* (No errors are found or errors have been fixed)
|
||||||
|
* -1 = Error(s) cannot be fixed
|
||||||
|
****************************************************************************/
|
||||||
|
int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
|
||||||
|
int numEccBytes)
|
||||||
|
{
|
||||||
|
int numErrors;
|
||||||
|
int errorLocation;
|
||||||
|
int idx;
|
||||||
|
uint32_t regValue;
|
||||||
|
|
||||||
|
/* wait for read ECC to be valid */
|
||||||
|
regValue = nand_bcm_umi_bch_poll_read_ecc_calc();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* read the control status register to determine if there
|
||||||
|
* are error'ed bits
|
||||||
|
* see if errors are correctible
|
||||||
|
*/
|
||||||
|
if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < numEccBytes; i++) {
|
||||||
|
if (readEccData[i] != 0xff) {
|
||||||
|
/* errors cannot be fixed, return -1 */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If ECC is unprogrammed then we can't correct,
|
||||||
|
* assume everything OK */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) {
|
||||||
|
/* no errors */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fix errored bits by doing the following:
|
||||||
|
* 1. Read the number of errors in the control and status register
|
||||||
|
* 2. Read the error location registers that corresponds to the number
|
||||||
|
* of errors reported
|
||||||
|
* 3. Invert the bit in the data
|
||||||
|
*/
|
||||||
|
numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20;
|
||||||
|
|
||||||
|
for (idx = 0; idx < numErrors; idx++) {
|
||||||
|
errorLocation =
|
||||||
|
REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK;
|
||||||
|
|
||||||
|
/* Flip bit */
|
||||||
|
nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation);
|
||||||
|
}
|
||||||
|
/* Errors corrected */
|
||||||
|
return numErrors;
|
||||||
|
}
|
||||||
|
#endif
|
358
drivers/mtd/nand/nand_bcm_umi.h
Normal file
358
drivers/mtd/nand/nand_bcm_umi.h
Normal file
|
@ -0,0 +1,358 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright 2003 - 2009 Broadcom Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* Unless you and Broadcom execute a separate written software license
|
||||||
|
* agreement governing use of this software, this software is licensed to you
|
||||||
|
* under the terms of the GNU General Public License version 2, available at
|
||||||
|
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
|
||||||
|
*
|
||||||
|
* Notwithstanding the above, under no circumstances may you combine this
|
||||||
|
* software in any way with any other Broadcom software provided under a
|
||||||
|
* license other than the GPL, without Broadcom's express prior written
|
||||||
|
* consent.
|
||||||
|
*****************************************************************************/
|
||||||
|
#ifndef NAND_BCM_UMI_H
|
||||||
|
#define NAND_BCM_UMI_H
|
||||||
|
|
||||||
|
/* ---- Include Files ---------------------------------------------------- */
|
||||||
|
#include <mach/reg_umi.h>
|
||||||
|
#include <mach/reg_nand.h>
|
||||||
|
#include <cfg_global.h>
|
||||||
|
|
||||||
|
/* ---- Constants and Types ---------------------------------------------- */
|
||||||
|
#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING)
|
||||||
|
#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0)
|
||||||
|
#else
|
||||||
|
#define NAND_ECC_BCH 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13
|
||||||
|
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
#ifdef BOOT0_BUILD
|
||||||
|
#define NAND_ECC_NUM_BYTES 13
|
||||||
|
#else
|
||||||
|
#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define NAND_ECC_NUM_BYTES 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NAND_DATA_ACCESS_SIZE 512
|
||||||
|
|
||||||
|
/* ---- Variable Externs ------------------------------------------ */
|
||||||
|
/* ---- Function Prototypes --------------------------------------- */
|
||||||
|
int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
|
||||||
|
int numEccBytes);
|
||||||
|
|
||||||
|
/* Check in device is ready */
|
||||||
|
static inline int nand_bcm_umi_dev_ready(void)
|
||||||
|
{
|
||||||
|
return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait until device is ready */
|
||||||
|
static inline void nand_bcm_umi_wait_till_ready(void)
|
||||||
|
{
|
||||||
|
while (nand_bcm_umi_dev_ready() == 0)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable Hamming ECC */
|
||||||
|
static inline void nand_bcm_umi_hamming_enable_hwecc(void)
|
||||||
|
{
|
||||||
|
/* disable and reset ECC, 512 byte page */
|
||||||
|
REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE |
|
||||||
|
REG_UMI_NAND_ECC_CSR_256BYTE);
|
||||||
|
/* enable ECC */
|
||||||
|
REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NAND_ECC_BCH
|
||||||
|
/* BCH ECC specifics */
|
||||||
|
#define ECC_BITS_PER_CORRECTABLE_BIT 13
|
||||||
|
|
||||||
|
/* Enable BCH Read ECC */
|
||||||
|
static inline void nand_bcm_umi_bch_enable_read_hwecc(void)
|
||||||
|
{
|
||||||
|
/* disable and reset ECC */
|
||||||
|
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID;
|
||||||
|
/* Turn on ECC */
|
||||||
|
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enable BCH Write ECC */
|
||||||
|
static inline void nand_bcm_umi_bch_enable_write_hwecc(void)
|
||||||
|
{
|
||||||
|
/* disable and reset ECC */
|
||||||
|
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID;
|
||||||
|
/* Turn on ECC */
|
||||||
|
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Config number of BCH ECC bytes */
|
||||||
|
static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes)
|
||||||
|
{
|
||||||
|
uint32_t nValue;
|
||||||
|
uint32_t tValue;
|
||||||
|
uint32_t kValue;
|
||||||
|
uint32_t numBits = numEccBytes * 8;
|
||||||
|
|
||||||
|
/* disable and reset ECC */
|
||||||
|
REG_UMI_BCH_CTRL_STATUS =
|
||||||
|
REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID |
|
||||||
|
REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID;
|
||||||
|
|
||||||
|
/* Every correctible bit requires 13 ECC bits */
|
||||||
|
tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT);
|
||||||
|
|
||||||
|
/* Total data in number of bits for generating and computing BCH ECC */
|
||||||
|
nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8;
|
||||||
|
|
||||||
|
/* K parameter is used internally. K = N - (T * 13) */
|
||||||
|
kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT);
|
||||||
|
|
||||||
|
/* Write the settings */
|
||||||
|
REG_UMI_BCH_N = nValue;
|
||||||
|
REG_UMI_BCH_T = tValue;
|
||||||
|
REG_UMI_BCH_K = kValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pause during ECC read calculation to skip bytes in OOB */
|
||||||
|
static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void)
|
||||||
|
{
|
||||||
|
REG_UMI_BCH_CTRL_STATUS =
|
||||||
|
REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN |
|
||||||
|
REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resume during ECC read calculation after skipping bytes in OOB */
|
||||||
|
static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void)
|
||||||
|
{
|
||||||
|
REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Poll read ECC calc to check when hardware completes */
|
||||||
|
static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void)
|
||||||
|
{
|
||||||
|
uint32_t regVal;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* wait for ECC to be valid */
|
||||||
|
regVal = REG_UMI_BCH_CTRL_STATUS;
|
||||||
|
} while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0);
|
||||||
|
|
||||||
|
return regVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Poll write ECC calc to check when hardware completes */
|
||||||
|
static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void)
|
||||||
|
{
|
||||||
|
/* wait for ECC to be valid */
|
||||||
|
while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID)
|
||||||
|
== 0)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the OOB and ECC, for kernel write OOB to a buffer */
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
|
||||||
|
uint8_t *eccCalc, int numEccBytes, uint8_t *oobp)
|
||||||
|
#else
|
||||||
|
static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
|
||||||
|
uint8_t *eccCalc, int numEccBytes)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int eccPos = 0;
|
||||||
|
int numToRead = 16; /* There are 16 bytes per sector in the OOB */
|
||||||
|
|
||||||
|
/* ECC is already paused when this function is called */
|
||||||
|
|
||||||
|
if (pageSize == NAND_DATA_ACCESS_SIZE) {
|
||||||
|
while (numToRead > numEccBytes) {
|
||||||
|
/* skip free oob region */
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp++ = REG_NAND_DATA8;
|
||||||
|
#else
|
||||||
|
REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
numToRead--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read ECC bytes before BI */
|
||||||
|
nand_bcm_umi_bch_resume_read_ecc_calc();
|
||||||
|
|
||||||
|
while (numToRead > 11) {
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp = REG_NAND_DATA8;
|
||||||
|
eccCalc[eccPos++] = *oobp;
|
||||||
|
oobp++;
|
||||||
|
#else
|
||||||
|
eccCalc[eccPos++] = REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_bcm_umi_bch_pause_read_ecc_calc();
|
||||||
|
|
||||||
|
if (numToRead == 11) {
|
||||||
|
/* read BI */
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp++ = REG_NAND_DATA8;
|
||||||
|
#else
|
||||||
|
REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
numToRead--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read ECC bytes */
|
||||||
|
nand_bcm_umi_bch_resume_read_ecc_calc();
|
||||||
|
while (numToRead) {
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp = REG_NAND_DATA8;
|
||||||
|
eccCalc[eccPos++] = *oobp;
|
||||||
|
oobp++;
|
||||||
|
#else
|
||||||
|
eccCalc[eccPos++] = REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
numToRead--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* skip BI */
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp++ = REG_NAND_DATA8;
|
||||||
|
#else
|
||||||
|
REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
numToRead--;
|
||||||
|
|
||||||
|
while (numToRead > numEccBytes) {
|
||||||
|
/* skip free oob region */
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp++ = REG_NAND_DATA8;
|
||||||
|
#else
|
||||||
|
REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
numToRead--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read ECC bytes */
|
||||||
|
nand_bcm_umi_bch_resume_read_ecc_calc();
|
||||||
|
while (numToRead) {
|
||||||
|
#if defined(__KERNEL__) && !defined(STANDALONE)
|
||||||
|
*oobp = REG_NAND_DATA8;
|
||||||
|
eccCalc[eccPos++] = *oobp;
|
||||||
|
oobp++;
|
||||||
|
#else
|
||||||
|
eccCalc[eccPos++] = REG_NAND_DATA8;
|
||||||
|
#endif
|
||||||
|
numToRead--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function to write ECC */
|
||||||
|
static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos,
|
||||||
|
uint8_t *oobp, uint8_t eccVal)
|
||||||
|
{
|
||||||
|
if (eccBytePos <= numEccBytes)
|
||||||
|
*oobp = eccVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write OOB with ECC */
|
||||||
|
static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize,
|
||||||
|
uint8_t *oobp, int numEccBytes)
|
||||||
|
{
|
||||||
|
uint32_t eccVal = 0xffffffff;
|
||||||
|
|
||||||
|
/* wait for write ECC to be valid */
|
||||||
|
nand_bcm_umi_bch_poll_write_ecc_calc();
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Get the hardware ecc from the 32-bit result registers.
|
||||||
|
** Read after 512 byte accesses. Format B3B2B1B0
|
||||||
|
** where B3 = ecc3, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (pageSize == NAND_DATA_ACCESS_SIZE) {
|
||||||
|
/* Now fill in the ECC bytes */
|
||||||
|
if (numEccBytes >= 13)
|
||||||
|
eccVal = REG_UMI_BCH_WR_ECC_3;
|
||||||
|
|
||||||
|
/* Usually we skip CM in oob[0,1] */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0],
|
||||||
|
(eccVal >> 16) & 0xff);
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1],
|
||||||
|
(eccVal >> 8) & 0xff);
|
||||||
|
|
||||||
|
/* Write ECC in oob[2,3,4] */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2],
|
||||||
|
eccVal & 0xff); /* ECC 12 */
|
||||||
|
|
||||||
|
if (numEccBytes >= 9)
|
||||||
|
eccVal = REG_UMI_BCH_WR_ECC_2;
|
||||||
|
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3],
|
||||||
|
(eccVal >> 24) & 0xff); /* ECC11 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4],
|
||||||
|
(eccVal >> 16) & 0xff); /* ECC10 */
|
||||||
|
|
||||||
|
/* Always Skip BI in oob[5] */
|
||||||
|
} else {
|
||||||
|
/* Always Skip BI in oob[0] */
|
||||||
|
|
||||||
|
/* Now fill in the ECC bytes */
|
||||||
|
if (numEccBytes >= 13)
|
||||||
|
eccVal = REG_UMI_BCH_WR_ECC_3;
|
||||||
|
|
||||||
|
/* Usually skip CM in oob[1,2] */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1],
|
||||||
|
(eccVal >> 16) & 0xff);
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2],
|
||||||
|
(eccVal >> 8) & 0xff);
|
||||||
|
|
||||||
|
/* Write ECC in oob[3-15] */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3],
|
||||||
|
eccVal & 0xff); /* ECC12 */
|
||||||
|
|
||||||
|
if (numEccBytes >= 9)
|
||||||
|
eccVal = REG_UMI_BCH_WR_ECC_2;
|
||||||
|
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4],
|
||||||
|
(eccVal >> 24) & 0xff); /* ECC11 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5],
|
||||||
|
(eccVal >> 16) & 0xff); /* ECC10 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill in the remainder of ECC locations */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6],
|
||||||
|
(eccVal >> 8) & 0xff); /* ECC9 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7],
|
||||||
|
eccVal & 0xff); /* ECC8 */
|
||||||
|
|
||||||
|
if (numEccBytes >= 5)
|
||||||
|
eccVal = REG_UMI_BCH_WR_ECC_1;
|
||||||
|
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8],
|
||||||
|
(eccVal >> 24) & 0xff); /* ECC7 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9],
|
||||||
|
(eccVal >> 16) & 0xff); /* ECC6 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10],
|
||||||
|
(eccVal >> 8) & 0xff); /* ECC5 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11],
|
||||||
|
eccVal & 0xff); /* ECC4 */
|
||||||
|
|
||||||
|
if (numEccBytes >= 1)
|
||||||
|
eccVal = REG_UMI_BCH_WR_ECC_0;
|
||||||
|
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12],
|
||||||
|
(eccVal >> 24) & 0xff); /* ECC3 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13],
|
||||||
|
(eccVal >> 16) & 0xff); /* ECC2 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14],
|
||||||
|
(eccVal >> 8) & 0xff); /* ECC1 */
|
||||||
|
NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15],
|
||||||
|
eccVal & 0xff); /* ECC0 */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* NAND_BCM_UMI_H */
|
|
@ -150,20 +150,19 @@ static const char addressbits[256] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
|
* __nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
|
||||||
* block
|
* block
|
||||||
* @mtd: MTD block structure
|
|
||||||
* @buf: input buffer with raw data
|
* @buf: input buffer with raw data
|
||||||
|
* @eccsize: data bytes per ecc step (256 or 512)
|
||||||
* @code: output buffer with ECC
|
* @code: output buffer with ECC
|
||||||
*/
|
*/
|
||||||
int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
|
void __nand_calculate_ecc(const unsigned char *buf, unsigned int eccsize,
|
||||||
unsigned char *code)
|
unsigned char *code)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
const uint32_t *bp = (uint32_t *)buf;
|
const uint32_t *bp = (uint32_t *)buf;
|
||||||
/* 256 or 512 bytes/ecc */
|
/* 256 or 512 bytes/ecc */
|
||||||
const uint32_t eccsize_mult =
|
const uint32_t eccsize_mult = eccsize >> 8;
|
||||||
(((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
|
|
||||||
uint32_t cur; /* current value in buffer */
|
uint32_t cur; /* current value in buffer */
|
||||||
/* rp0..rp15..rp17 are the various accumulated parities (per byte) */
|
/* rp0..rp15..rp17 are the various accumulated parities (per byte) */
|
||||||
uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
|
uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7;
|
||||||
|
@ -412,6 +411,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
|
||||||
(invparity[par & 0x55] << 2) |
|
(invparity[par & 0x55] << 2) |
|
||||||
(invparity[rp17] << 1) |
|
(invparity[rp17] << 1) |
|
||||||
(invparity[rp16] << 0);
|
(invparity[rp16] << 0);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__nand_calculate_ecc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte
|
||||||
|
* block
|
||||||
|
* @mtd: MTD block structure
|
||||||
|
* @buf: input buffer with raw data
|
||||||
|
* @code: output buffer with ECC
|
||||||
|
*/
|
||||||
|
int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
|
||||||
|
unsigned char *code)
|
||||||
|
{
|
||||||
|
__nand_calculate_ecc(buf,
|
||||||
|
((struct nand_chip *)mtd->priv)->ecc.size, code);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(nand_calculate_ecc);
|
EXPORT_SYMBOL(nand_calculate_ecc);
|
||||||
|
|
|
@ -161,7 +161,7 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I
|
||||||
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
|
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
|
||||||
|
|
||||||
/* The largest possible page size */
|
/* The largest possible page size */
|
||||||
#define NS_LARGEST_PAGE_SIZE 2048
|
#define NS_LARGEST_PAGE_SIZE 4096
|
||||||
|
|
||||||
/* The prefix for simulator output */
|
/* The prefix for simulator output */
|
||||||
#define NS_OUTPUT_PREFIX "[nandsim]"
|
#define NS_OUTPUT_PREFIX "[nandsim]"
|
||||||
|
@ -259,7 +259,8 @@ MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of mem
|
||||||
#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */
|
#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */
|
||||||
#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */
|
#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */
|
||||||
#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
|
#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
|
||||||
#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */
|
#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */
|
||||||
|
#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
|
||||||
#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */
|
#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */
|
||||||
|
|
||||||
/* Remove action bits ftom state */
|
/* Remove action bits ftom state */
|
||||||
|
@ -588,6 +589,8 @@ static int init_nandsim(struct mtd_info *mtd)
|
||||||
ns->options |= OPT_PAGE512_8BIT;
|
ns->options |= OPT_PAGE512_8BIT;
|
||||||
} else if (ns->geom.pgsz == 2048) {
|
} else if (ns->geom.pgsz == 2048) {
|
||||||
ns->options |= OPT_PAGE2048;
|
ns->options |= OPT_PAGE2048;
|
||||||
|
} else if (ns->geom.pgsz == 4096) {
|
||||||
|
ns->options |= OPT_PAGE4096;
|
||||||
} else {
|
} else {
|
||||||
NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
|
NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
|
@ -34,7 +34,12 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
||||||
struct plat_nand_data *data;
|
struct plat_nand_data *data;
|
||||||
int res = 0;
|
struct resource *res;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res)
|
||||||
|
return -ENXIO;
|
||||||
|
|
||||||
/* Allocate memory for the device structure (and zero it) */
|
/* Allocate memory for the device structure (and zero it) */
|
||||||
data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL);
|
data = kzalloc(sizeof(struct plat_nand_data), GFP_KERNEL);
|
||||||
|
@ -43,12 +48,18 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->io_base = ioremap(pdev->resource[0].start,
|
if (!request_mem_region(res->start, resource_size(res),
|
||||||
pdev->resource[0].end - pdev->resource[0].start + 1);
|
dev_name(&pdev->dev))) {
|
||||||
|
dev_err(&pdev->dev, "request_mem_region failed\n");
|
||||||
|
err = -EBUSY;
|
||||||
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->io_base = ioremap(res->start, resource_size(res));
|
||||||
if (data->io_base == NULL) {
|
if (data->io_base == NULL) {
|
||||||
dev_err(&pdev->dev, "ioremap failed\n");
|
dev_err(&pdev->dev, "ioremap failed\n");
|
||||||
kfree(data);
|
err = -EIO;
|
||||||
return -EIO;
|
goto out_release_io;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->chip.priv = &data;
|
data->chip.priv = &data;
|
||||||
|
@ -74,24 +85,24 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* Handle any platform specific setup */
|
/* Handle any platform specific setup */
|
||||||
if (pdata->ctrl.probe) {
|
if (pdata->ctrl.probe) {
|
||||||
res = pdata->ctrl.probe(pdev);
|
err = pdata->ctrl.probe(pdev);
|
||||||
if (res)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan to find existance of the device */
|
/* Scan to find existance of the device */
|
||||||
if (nand_scan(&data->mtd, 1)) {
|
if (nand_scan(&data->mtd, 1)) {
|
||||||
res = -ENXIO;
|
err = -ENXIO;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
if (pdata->chip.part_probe_types) {
|
if (pdata->chip.part_probe_types) {
|
||||||
res = parse_mtd_partitions(&data->mtd,
|
err = parse_mtd_partitions(&data->mtd,
|
||||||
pdata->chip.part_probe_types,
|
pdata->chip.part_probe_types,
|
||||||
&data->parts, 0);
|
&data->parts, 0);
|
||||||
if (res > 0) {
|
if (err > 0) {
|
||||||
add_mtd_partitions(&data->mtd, data->parts, res);
|
add_mtd_partitions(&data->mtd, data->parts, err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,14 +110,14 @@ static int __devinit plat_nand_probe(struct platform_device *pdev)
|
||||||
pdata->chip.set_parts(data->mtd.size, &pdata->chip);
|
pdata->chip.set_parts(data->mtd.size, &pdata->chip);
|
||||||
if (pdata->chip.partitions) {
|
if (pdata->chip.partitions) {
|
||||||
data->parts = pdata->chip.partitions;
|
data->parts = pdata->chip.partitions;
|
||||||
res = add_mtd_partitions(&data->mtd, data->parts,
|
err = add_mtd_partitions(&data->mtd, data->parts,
|
||||||
pdata->chip.nr_partitions);
|
pdata->chip.nr_partitions);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
res = add_mtd_device(&data->mtd);
|
err = add_mtd_device(&data->mtd);
|
||||||
|
|
||||||
if (!res)
|
if (!err)
|
||||||
return res;
|
return err;
|
||||||
|
|
||||||
nand_release(&data->mtd);
|
nand_release(&data->mtd);
|
||||||
out:
|
out:
|
||||||
|
@ -114,8 +125,11 @@ out:
|
||||||
pdata->ctrl.remove(pdev);
|
pdata->ctrl.remove(pdev);
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
iounmap(data->io_base);
|
iounmap(data->io_base);
|
||||||
|
out_release_io:
|
||||||
|
release_mem_region(res->start, resource_size(res));
|
||||||
|
out_free:
|
||||||
kfree(data);
|
kfree(data);
|
||||||
return res;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -125,6 +139,9 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
struct plat_nand_data *data = platform_get_drvdata(pdev);
|
||||||
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
struct platform_nand_data *pdata = pdev->dev.platform_data;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
|
||||||
nand_release(&data->mtd);
|
nand_release(&data->mtd);
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
|
@ -134,6 +151,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev)
|
||||||
if (pdata->ctrl.remove)
|
if (pdata->ctrl.remove)
|
||||||
pdata->ctrl.remove(pdev);
|
pdata->ctrl.remove(pdev);
|
||||||
iounmap(data->io_base);
|
iounmap(data->io_base);
|
||||||
|
release_mem_region(res->start, resource_size(res));
|
||||||
kfree(data);
|
kfree(data);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -774,7 +774,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||||
chip->select_chip = s3c2410_nand_select_chip;
|
chip->select_chip = s3c2410_nand_select_chip;
|
||||||
chip->chip_delay = 50;
|
chip->chip_delay = 50;
|
||||||
chip->priv = nmtd;
|
chip->priv = nmtd;
|
||||||
chip->options = 0;
|
chip->options = set->options;
|
||||||
chip->controller = &info->controller;
|
chip->controller = &info->controller;
|
||||||
|
|
||||||
switch (info->cpu_type) {
|
switch (info->cpu_type) {
|
||||||
|
|
|
@ -429,11 +429,10 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
|
||||||
chip = mtd->priv;
|
chip = mtd->priv;
|
||||||
txx9_priv = chip->priv;
|
txx9_priv = chip->priv;
|
||||||
|
|
||||||
|
nand_release(mtd);
|
||||||
#ifdef CONFIG_MTD_PARTITIONS
|
#ifdef CONFIG_MTD_PARTITIONS
|
||||||
del_mtd_partitions(mtd);
|
|
||||||
kfree(drvdata->parts[i]);
|
kfree(drvdata->parts[i]);
|
||||||
#endif
|
#endif
|
||||||
del_mtd_device(mtd);
|
|
||||||
kfree(txx9_priv->mtdname);
|
kfree(txx9_priv->mtdname);
|
||||||
kfree(txx9_priv);
|
kfree(txx9_priv);
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,10 +112,24 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
u32 syscfg;
|
u32 syscfg;
|
||||||
|
|
||||||
if (state == FL_RESETING) {
|
if (state == FL_RESETING || state == FL_PREPARING_ERASE ||
|
||||||
int i;
|
state == FL_VERIFYING_ERASE) {
|
||||||
|
int i = 21;
|
||||||
|
unsigned int intr_flags = ONENAND_INT_MASTER;
|
||||||
|
|
||||||
for (i = 0; i < 20; i++) {
|
switch (state) {
|
||||||
|
case FL_RESETING:
|
||||||
|
intr_flags |= ONENAND_INT_RESET;
|
||||||
|
break;
|
||||||
|
case FL_PREPARING_ERASE:
|
||||||
|
intr_flags |= ONENAND_INT_ERASE;
|
||||||
|
break;
|
||||||
|
case FL_VERIFYING_ERASE:
|
||||||
|
i = 101;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (--i) {
|
||||||
udelay(1);
|
udelay(1);
|
||||||
intr = read_reg(c, ONENAND_REG_INTERRUPT);
|
intr = read_reg(c, ONENAND_REG_INTERRUPT);
|
||||||
if (intr & ONENAND_INT_MASTER)
|
if (intr & ONENAND_INT_MASTER)
|
||||||
|
@ -126,7 +140,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state)
|
||||||
wait_err("controller error", state, ctrl, intr);
|
wait_err("controller error", state, ctrl, intr);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
if (!(intr & ONENAND_INT_RESET)) {
|
if ((intr & intr_flags) != intr_flags) {
|
||||||
wait_err("timeout", state, ctrl, intr);
|
wait_err("timeout", state, ctrl, intr);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,3 +5,4 @@ obj-$(CONFIG_MTD_TESTS) += mtd_speedtest.o
|
||||||
obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o
|
obj-$(CONFIG_MTD_TESTS) += mtd_stresstest.o
|
||||||
obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
|
obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o
|
||||||
obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
|
obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o
|
||||||
|
obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o
|
||||||
|
|
87
drivers/mtd/tests/mtd_nandecctest.c
Normal file
87
drivers/mtd/tests/mtd_nandecctest.c
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/random.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/mtd/nand_ecc.h>
|
||||||
|
|
||||||
|
#if defined(CONFIG_MTD_NAND) || defined(CONFIG_MTD_NAND_MODULE)
|
||||||
|
|
||||||
|
static void inject_single_bit_error(void *data, size_t size)
|
||||||
|
{
|
||||||
|
unsigned long offset = random32() % (size * BITS_PER_BYTE);
|
||||||
|
|
||||||
|
__change_bit(offset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char data[512];
|
||||||
|
static unsigned char error_data[512];
|
||||||
|
|
||||||
|
static int nand_ecc_test(const size_t size)
|
||||||
|
{
|
||||||
|
unsigned char code[3];
|
||||||
|
unsigned char error_code[3];
|
||||||
|
char testname[30];
|
||||||
|
|
||||||
|
BUG_ON(sizeof(data) < size);
|
||||||
|
|
||||||
|
sprintf(testname, "nand-ecc-%zu", size);
|
||||||
|
|
||||||
|
get_random_bytes(data, size);
|
||||||
|
|
||||||
|
memcpy(error_data, data, size);
|
||||||
|
inject_single_bit_error(error_data, size);
|
||||||
|
|
||||||
|
__nand_calculate_ecc(data, size, code);
|
||||||
|
__nand_calculate_ecc(error_data, size, error_code);
|
||||||
|
__nand_correct_data(error_data, code, error_code, size);
|
||||||
|
|
||||||
|
if (!memcmp(data, error_data, size)) {
|
||||||
|
printk(KERN_INFO "mtd_nandecctest: ok - %s\n", testname);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
printk(KERN_ERR "mtd_nandecctest: not ok - %s\n", testname);
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "hexdump of data:\n");
|
||||||
|
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4,
|
||||||
|
data, size, false);
|
||||||
|
printk(KERN_DEBUG "hexdump of error data:\n");
|
||||||
|
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 4,
|
||||||
|
error_data, size, false);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
static int nand_ecc_test(const size_t size)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int __init ecc_test_init(void)
|
||||||
|
{
|
||||||
|
srandom32(jiffies);
|
||||||
|
|
||||||
|
nand_ecc_test(256);
|
||||||
|
nand_ecc_test(512);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit ecc_test_exit(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(ecc_test_init);
|
||||||
|
module_exit(ecc_test_exit);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("NAND ECC function test module");
|
||||||
|
MODULE_AUTHOR("Akinobu Mita");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -343,7 +343,6 @@ static int scan_for_bad_eraseblocks(void)
|
||||||
printk(PRINT_PREF "error: cannot allocate memory\n");
|
printk(PRINT_PREF "error: cannot allocate memory\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
memset(bbt, 0 , ebcnt);
|
|
||||||
|
|
||||||
printk(PRINT_PREF "scanning for bad eraseblocks\n");
|
printk(PRINT_PREF "scanning for bad eraseblocks\n");
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
for (i = 0; i < ebcnt; ++i) {
|
||||||
|
@ -392,7 +391,6 @@ static int __init mtd_oobtest_init(void)
|
||||||
mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
|
mtd->writesize, ebcnt, pgcnt, mtd->oobsize);
|
||||||
|
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
mtd->erasesize = mtd->erasesize;
|
|
||||||
readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
readbuf = kmalloc(mtd->erasesize, GFP_KERNEL);
|
||||||
if (!readbuf) {
|
if (!readbuf) {
|
||||||
printk(PRINT_PREF "error: cannot allocate memory\n");
|
printk(PRINT_PREF "error: cannot allocate memory\n");
|
||||||
|
@ -476,18 +474,10 @@ static int __init mtd_oobtest_init(void)
|
||||||
use_len_max = mtd->ecclayout->oobavail;
|
use_len_max = mtd->ecclayout->oobavail;
|
||||||
vary_offset = 1;
|
vary_offset = 1;
|
||||||
simple_srand(5);
|
simple_srand(5);
|
||||||
printk(PRINT_PREF "writing OOBs of whole device\n");
|
|
||||||
for (i = 0; i < ebcnt; ++i) {
|
err = write_whole_device();
|
||||||
if (bbt[i])
|
if (err)
|
||||||
continue;
|
goto out;
|
||||||
err = write_eraseblock(i);
|
|
||||||
if (err)
|
|
||||||
goto out;
|
|
||||||
if (i % 256 == 0)
|
|
||||||
printk(PRINT_PREF "written up to eraseblock %u\n", i);
|
|
||||||
cond_resched();
|
|
||||||
}
|
|
||||||
printk(PRINT_PREF "written %u eraseblocks\n", i);
|
|
||||||
|
|
||||||
/* Check all eraseblocks */
|
/* Check all eraseblocks */
|
||||||
use_offset = 0;
|
use_offset = 0;
|
||||||
|
|
|
@ -523,6 +523,7 @@ static int __init mtd_pagetest_init(void)
|
||||||
do_div(tmp, mtd->erasesize);
|
do_div(tmp, mtd->erasesize);
|
||||||
ebcnt = tmp;
|
ebcnt = tmp;
|
||||||
pgcnt = mtd->erasesize / mtd->writesize;
|
pgcnt = mtd->erasesize / mtd->writesize;
|
||||||
|
pgsize = mtd->writesize;
|
||||||
|
|
||||||
printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
|
printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, "
|
||||||
"page size %u, count of eraseblocks %u, pages per "
|
"page size %u, count of eraseblocks %u, pages per "
|
||||||
|
|
|
@ -700,7 +700,8 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
|
||||||
struct jffs2_raw_inode ri;
|
struct jffs2_raw_inode ri;
|
||||||
struct jffs2_node_frag *last_frag;
|
struct jffs2_node_frag *last_frag;
|
||||||
union jffs2_device_node dev;
|
union jffs2_device_node dev;
|
||||||
char *mdata = NULL, mdatalen = 0;
|
char *mdata = NULL;
|
||||||
|
int mdatalen = 0;
|
||||||
uint32_t alloclen, ilen;
|
uint32_t alloclen, ilen;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|
|
@ -1284,7 +1284,7 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
||||||
f->target = NULL;
|
f->target = NULL;
|
||||||
mutex_unlock(&f->sem);
|
mutex_unlock(&f->sem);
|
||||||
jffs2_do_clear_inode(c, f);
|
jffs2_do_clear_inode(c, f);
|
||||||
return -ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
f->target[je32_to_cpu(latest_node->csize)] = '\0';
|
f->target[je32_to_cpu(latest_node->csize)] = '\0';
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
int jffs2_sum_init(struct jffs2_sb_info *c)
|
int jffs2_sum_init(struct jffs2_sb_info *c)
|
||||||
{
|
{
|
||||||
uint32_t sum_size = max_t(uint32_t, c->sector_size, MAX_SUMMARY_SIZE);
|
uint32_t sum_size = min_t(uint32_t, c->sector_size, MAX_SUMMARY_SIZE);
|
||||||
|
|
||||||
c->summary = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
|
c->summary = kzalloc(sizeof(struct jffs2_summary), GFP_KERNEL);
|
||||||
|
|
||||||
|
|
60
include/linux/kmsg_dump.h
Normal file
60
include/linux/kmsg_dump.h
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* linux/include/kmsg_dump.h
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Net Insight AB
|
||||||
|
*
|
||||||
|
* Author: Simon Kagstrom <simon.kagstrom@netinsight.net>
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU General Public
|
||||||
|
* License. See the file COPYING in the main directory of this archive
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
#ifndef _LINUX_KMSG_DUMP_H
|
||||||
|
#define _LINUX_KMSG_DUMP_H
|
||||||
|
|
||||||
|
#include <linux/list.h>
|
||||||
|
|
||||||
|
enum kmsg_dump_reason {
|
||||||
|
KMSG_DUMP_OOPS,
|
||||||
|
KMSG_DUMP_PANIC,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct kmsg_dumper - kernel crash message dumper structure
|
||||||
|
* @dump: The callback which gets called on crashes. The buffer is passed
|
||||||
|
* as two sections, where s1 (length l1) contains the older
|
||||||
|
* messages and s2 (length l2) contains the newer.
|
||||||
|
* @list: Entry in the dumper list (private)
|
||||||
|
* @registered: Flag that specifies if this is already registered
|
||||||
|
*/
|
||||||
|
struct kmsg_dumper {
|
||||||
|
void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
|
||||||
|
const char *s1, unsigned long l1,
|
||||||
|
const char *s2, unsigned long l2);
|
||||||
|
struct list_head list;
|
||||||
|
int registered;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PRINTK
|
||||||
|
void kmsg_dump(enum kmsg_dump_reason reason);
|
||||||
|
|
||||||
|
int kmsg_dump_register(struct kmsg_dumper *dumper);
|
||||||
|
|
||||||
|
int kmsg_dump_unregister(struct kmsg_dumper *dumper);
|
||||||
|
#else
|
||||||
|
static inline void kmsg_dump(enum kmsg_dump_reason reason)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int kmsg_dump_register(struct kmsg_dumper *dumper)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int kmsg_dump_unregister(struct kmsg_dumper *dumper)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _LINUX_KMSG_DUMP_H */
|
|
@ -19,22 +19,21 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct nand_bbt_descr - bad block table descriptor
|
* struct nand_bbt_descr - bad block table descriptor
|
||||||
* @options: options for this descriptor
|
* @options: options for this descriptor
|
||||||
* @pages: the page(s) where we find the bbt, used with
|
* @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
|
||||||
* option BBT_ABSPAGE when bbt is searched,
|
* when bbt is searched, then we store the found bbts pages here.
|
||||||
* then we store the found bbts pages here.
|
* Its an array and supports up to 8 chips now
|
||||||
* Its an array and supports up to 8 chips now
|
* @offs: offset of the pattern in the oob area of the page
|
||||||
* @offs: offset of the pattern in the oob area of the page
|
* @veroffs: offset of the bbt version counter in the oob are of the page
|
||||||
* @veroffs: offset of the bbt version counter in the oob area of the page
|
* @version: version read from the bbt page during scan
|
||||||
* @version: version read from the bbt page during scan
|
* @len: length of the pattern, if 0 no pattern check is performed
|
||||||
* @len: length of the pattern, if 0 no pattern check is performed
|
* @maxblocks: maximum number of blocks to search for a bbt. This number of
|
||||||
* @maxblocks: maximum number of blocks to search for a bbt. This
|
* blocks is reserved at the end of the device where the tables are
|
||||||
* number of blocks is reserved at the end of the device
|
* written.
|
||||||
* where the tables are written.
|
* @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
|
||||||
* @reserved_block_code: if non-0, this pattern denotes a reserved
|
* bad) block in the stored bbt
|
||||||
* (rather than bad) block in the stored bbt
|
* @pattern: pattern to identify bad block table or factory marked good /
|
||||||
* @pattern: pattern to identify bad block table or factory marked
|
* bad blocks, can be NULL, if len = 0
|
||||||
* good / bad blocks, can be NULL, if len = 0
|
|
||||||
*
|
*
|
||||||
* Descriptor for the bad block table marker and the descriptor for the
|
* Descriptor for the bad block table marker and the descriptor for the
|
||||||
* pattern which identifies good and bad blocks. The assumption is made
|
* pattern which identifies good and bad blocks. The assumption is made
|
||||||
|
@ -90,7 +89,9 @@ struct nand_bbt_descr {
|
||||||
/*
|
/*
|
||||||
* Constants for oob configuration
|
* Constants for oob configuration
|
||||||
*/
|
*/
|
||||||
#define ONENAND_BADBLOCK_POS 0
|
#define NAND_SMALL_BADBLOCK_POS 5
|
||||||
|
#define NAND_LARGE_BADBLOCK_POS 0
|
||||||
|
#define ONENAND_BADBLOCK_POS 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bad block scanning errors
|
* Bad block scanning errors
|
||||||
|
|
|
@ -518,10 +518,11 @@ struct cfi_fixup {
|
||||||
#define CFI_MFR_ANY 0xffff
|
#define CFI_MFR_ANY 0xffff
|
||||||
#define CFI_ID_ANY 0xffff
|
#define CFI_ID_ANY 0xffff
|
||||||
|
|
||||||
#define CFI_MFR_AMD 0x0001
|
#define CFI_MFR_AMD 0x0001
|
||||||
#define CFI_MFR_ATMEL 0x001F
|
#define CFI_MFR_INTEL 0x0089
|
||||||
#define CFI_MFR_SAMSUNG 0x00EC
|
#define CFI_MFR_ATMEL 0x001F
|
||||||
#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
|
#define CFI_MFR_SAMSUNG 0x00EC
|
||||||
|
#define CFI_MFR_ST 0x0020 /* STMicroelectronics */
|
||||||
|
|
||||||
void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
|
void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,15 @@ typedef enum {
|
||||||
FL_XIP_WHILE_ERASING,
|
FL_XIP_WHILE_ERASING,
|
||||||
FL_XIP_WHILE_WRITING,
|
FL_XIP_WHILE_WRITING,
|
||||||
FL_SHUTDOWN,
|
FL_SHUTDOWN,
|
||||||
|
/* These 2 come from nand_state_t, which has been unified here */
|
||||||
|
FL_READING,
|
||||||
|
FL_CACHEDPRG,
|
||||||
|
/* These 4 come from onenand_state_t, which has been unified here */
|
||||||
|
FL_RESETING,
|
||||||
|
FL_OTPING,
|
||||||
|
FL_PREPARING_ERASE,
|
||||||
|
FL_VERIFYING_ERASE,
|
||||||
|
|
||||||
FL_UNKNOWN
|
FL_UNKNOWN
|
||||||
} flstate_t;
|
} flstate_t;
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/mtd/flashchip.h>
|
||||||
|
#include <linux/mtd/bbm.h>
|
||||||
|
|
||||||
struct mtd_info;
|
struct mtd_info;
|
||||||
/* Scan and identify a NAND device */
|
/* Scan and identify a NAND device */
|
||||||
|
@ -168,7 +170,6 @@ typedef enum {
|
||||||
/* Chip does not allow subpage writes */
|
/* Chip does not allow subpage writes */
|
||||||
#define NAND_NO_SUBPAGE_WRITE 0x00000200
|
#define NAND_NO_SUBPAGE_WRITE 0x00000200
|
||||||
|
|
||||||
|
|
||||||
/* Options valid for Samsung large page devices */
|
/* Options valid for Samsung large page devices */
|
||||||
#define NAND_SAMSUNG_LP_OPTIONS \
|
#define NAND_SAMSUNG_LP_OPTIONS \
|
||||||
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
||||||
|
@ -194,6 +195,9 @@ typedef enum {
|
||||||
/* This option is defined if the board driver allocates its own buffers
|
/* This option is defined if the board driver allocates its own buffers
|
||||||
(e.g. because it needs them DMA-coherent */
|
(e.g. because it needs them DMA-coherent */
|
||||||
#define NAND_OWN_BUFFERS 0x00040000
|
#define NAND_OWN_BUFFERS 0x00040000
|
||||||
|
/* Chip may not exist, so silence any errors in scan */
|
||||||
|
#define NAND_SCAN_SILENT_NODEV 0x00080000
|
||||||
|
|
||||||
/* Options set by nand scan */
|
/* Options set by nand scan */
|
||||||
/* Nand scan has allocated controller struct */
|
/* Nand scan has allocated controller struct */
|
||||||
#define NAND_CONTROLLER_ALLOC 0x80000000
|
#define NAND_CONTROLLER_ALLOC 0x80000000
|
||||||
|
@ -202,20 +206,6 @@ typedef enum {
|
||||||
#define NAND_CI_CHIPNR_MSK 0x03
|
#define NAND_CI_CHIPNR_MSK 0x03
|
||||||
#define NAND_CI_CELLTYPE_MSK 0x0C
|
#define NAND_CI_CELLTYPE_MSK 0x0C
|
||||||
|
|
||||||
/*
|
|
||||||
* nand_state_t - chip states
|
|
||||||
* Enumeration for NAND flash chip state
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
FL_READY,
|
|
||||||
FL_READING,
|
|
||||||
FL_WRITING,
|
|
||||||
FL_ERASING,
|
|
||||||
FL_SYNCING,
|
|
||||||
FL_CACHEDPRG,
|
|
||||||
FL_PM_SUSPENDED,
|
|
||||||
} nand_state_t;
|
|
||||||
|
|
||||||
/* Keep gcc happy */
|
/* Keep gcc happy */
|
||||||
struct nand_chip;
|
struct nand_chip;
|
||||||
|
|
||||||
|
@ -402,7 +392,7 @@ struct nand_chip {
|
||||||
uint8_t cellinfo;
|
uint8_t cellinfo;
|
||||||
int badblockpos;
|
int badblockpos;
|
||||||
|
|
||||||
nand_state_t state;
|
flstate_t state;
|
||||||
|
|
||||||
uint8_t *oob_poi;
|
uint8_t *oob_poi;
|
||||||
struct nand_hw_control *controller;
|
struct nand_hw_control *controller;
|
||||||
|
@ -470,75 +460,6 @@ struct nand_manufacturers {
|
||||||
extern struct nand_flash_dev nand_flash_ids[];
|
extern struct nand_flash_dev nand_flash_ids[];
|
||||||
extern struct nand_manufacturers nand_manuf_ids[];
|
extern struct nand_manufacturers nand_manuf_ids[];
|
||||||
|
|
||||||
/**
|
|
||||||
* struct nand_bbt_descr - bad block table descriptor
|
|
||||||
* @options: options for this descriptor
|
|
||||||
* @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
|
|
||||||
* when bbt is searched, then we store the found bbts pages here.
|
|
||||||
* Its an array and supports up to 8 chips now
|
|
||||||
* @offs: offset of the pattern in the oob area of the page
|
|
||||||
* @veroffs: offset of the bbt version counter in the oob are of the page
|
|
||||||
* @version: version read from the bbt page during scan
|
|
||||||
* @len: length of the pattern, if 0 no pattern check is performed
|
|
||||||
* @maxblocks: maximum number of blocks to search for a bbt. This number of
|
|
||||||
* blocks is reserved at the end of the device where the tables are
|
|
||||||
* written.
|
|
||||||
* @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
|
|
||||||
* bad) block in the stored bbt
|
|
||||||
* @pattern: pattern to identify bad block table or factory marked good /
|
|
||||||
* bad blocks, can be NULL, if len = 0
|
|
||||||
*
|
|
||||||
* Descriptor for the bad block table marker and the descriptor for the
|
|
||||||
* pattern which identifies good and bad blocks. The assumption is made
|
|
||||||
* that the pattern and the version count are always located in the oob area
|
|
||||||
* of the first block.
|
|
||||||
*/
|
|
||||||
struct nand_bbt_descr {
|
|
||||||
int options;
|
|
||||||
int pages[NAND_MAX_CHIPS];
|
|
||||||
int offs;
|
|
||||||
int veroffs;
|
|
||||||
uint8_t version[NAND_MAX_CHIPS];
|
|
||||||
int len;
|
|
||||||
int maxblocks;
|
|
||||||
int reserved_block_code;
|
|
||||||
uint8_t *pattern;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Options for the bad block table descriptors */
|
|
||||||
|
|
||||||
/* The number of bits used per block in the bbt on the device */
|
|
||||||
#define NAND_BBT_NRBITS_MSK 0x0000000F
|
|
||||||
#define NAND_BBT_1BIT 0x00000001
|
|
||||||
#define NAND_BBT_2BIT 0x00000002
|
|
||||||
#define NAND_BBT_4BIT 0x00000004
|
|
||||||
#define NAND_BBT_8BIT 0x00000008
|
|
||||||
/* The bad block table is in the last good block of the device */
|
|
||||||
#define NAND_BBT_LASTBLOCK 0x00000010
|
|
||||||
/* The bbt is at the given page, else we must scan for the bbt */
|
|
||||||
#define NAND_BBT_ABSPAGE 0x00000020
|
|
||||||
/* The bbt is at the given page, else we must scan for the bbt */
|
|
||||||
#define NAND_BBT_SEARCH 0x00000040
|
|
||||||
/* bbt is stored per chip on multichip devices */
|
|
||||||
#define NAND_BBT_PERCHIP 0x00000080
|
|
||||||
/* bbt has a version counter at offset veroffs */
|
|
||||||
#define NAND_BBT_VERSION 0x00000100
|
|
||||||
/* Create a bbt if none axists */
|
|
||||||
#define NAND_BBT_CREATE 0x00000200
|
|
||||||
/* Search good / bad pattern through all pages of a block */
|
|
||||||
#define NAND_BBT_SCANALLPAGES 0x00000400
|
|
||||||
/* Scan block empty during good / bad block scan */
|
|
||||||
#define NAND_BBT_SCANEMPTY 0x00000800
|
|
||||||
/* Write bbt if neccecary */
|
|
||||||
#define NAND_BBT_WRITE 0x00001000
|
|
||||||
/* Read and write back block contents when writing bbt */
|
|
||||||
#define NAND_BBT_SAVECONTENT 0x00002000
|
|
||||||
/* Search good / bad pattern on the first and the second page */
|
|
||||||
#define NAND_BBT_SCAN2NDPAGE 0x00004000
|
|
||||||
|
|
||||||
/* The maximum number of blocks to scan for a bbt */
|
|
||||||
#define NAND_BBT_SCAN_MAXBLOCKS 4
|
|
||||||
|
|
||||||
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
|
||||||
extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
|
extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
|
||||||
extern int nand_default_bbt(struct mtd_info *mtd);
|
extern int nand_default_bbt(struct mtd_info *mtd);
|
||||||
|
@ -548,12 +469,6 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
||||||
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
|
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);
|
||||||
|
|
||||||
/*
|
|
||||||
* Constants for oob configuration
|
|
||||||
*/
|
|
||||||
#define NAND_SMALL_BADBLOCK_POS 5
|
|
||||||
#define NAND_LARGE_BADBLOCK_POS 0
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct platform_nand_chip - chip level device structure
|
* struct platform_nand_chip - chip level device structure
|
||||||
* @nr_chips: max. number of chips to scan for
|
* @nr_chips: max. number of chips to scan for
|
||||||
|
|
|
@ -16,7 +16,13 @@
|
||||||
struct mtd_info;
|
struct mtd_info;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate 3 byte ECC code for 256 byte block
|
* Calculate 3 byte ECC code for eccsize byte block
|
||||||
|
*/
|
||||||
|
void __nand_calculate_ecc(const u_char *dat, unsigned int eccsize,
|
||||||
|
u_char *ecc_code);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate 3 byte ECC code for 256/512 byte block
|
||||||
*/
|
*/
|
||||||
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
|
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
|
||||||
|
|
||||||
|
@ -27,7 +33,7 @@ int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc,
|
||||||
unsigned int eccsize);
|
unsigned int eccsize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect and correct a 1 bit error for 256 byte block
|
* Detect and correct a 1 bit error for 256/512 byte block
|
||||||
*/
|
*/
|
||||||
int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
|
int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* linux/include/linux/mtd/onenand.h
|
* linux/include/linux/mtd/onenand.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005-2007 Samsung Electronics
|
* Copyright © 2005-2009 Samsung Electronics
|
||||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
|
#include <linux/mtd/flashchip.h>
|
||||||
#include <linux/mtd/onenand_regs.h>
|
#include <linux/mtd/onenand_regs.h>
|
||||||
#include <linux/mtd/bbm.h>
|
#include <linux/mtd/bbm.h>
|
||||||
|
|
||||||
|
@ -25,22 +26,6 @@ extern int onenand_scan(struct mtd_info *mtd, int max_chips);
|
||||||
/* Free resources held by the OneNAND device */
|
/* Free resources held by the OneNAND device */
|
||||||
extern void onenand_release(struct mtd_info *mtd);
|
extern void onenand_release(struct mtd_info *mtd);
|
||||||
|
|
||||||
/*
|
|
||||||
* onenand_state_t - chip states
|
|
||||||
* Enumeration for OneNAND flash chip state
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
FL_READY,
|
|
||||||
FL_READING,
|
|
||||||
FL_WRITING,
|
|
||||||
FL_ERASING,
|
|
||||||
FL_SYNCING,
|
|
||||||
FL_LOCKING,
|
|
||||||
FL_RESETING,
|
|
||||||
FL_OTPING,
|
|
||||||
FL_PM_SUSPENDED,
|
|
||||||
} onenand_state_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct onenand_bufferram - OneNAND BufferRAM Data
|
* struct onenand_bufferram - OneNAND BufferRAM Data
|
||||||
* @blockpage: block & page address in BufferRAM
|
* @blockpage: block & page address in BufferRAM
|
||||||
|
@ -137,7 +122,7 @@ struct onenand_chip {
|
||||||
|
|
||||||
spinlock_t chip_lock;
|
spinlock_t chip_lock;
|
||||||
wait_queue_head_t wq;
|
wait_queue_head_t wq;
|
||||||
onenand_state_t state;
|
flstate_t state;
|
||||||
unsigned char *page_buf;
|
unsigned char *page_buf;
|
||||||
unsigned char *oob_buf;
|
unsigned char *oob_buf;
|
||||||
|
|
||||||
|
@ -152,6 +137,8 @@ struct onenand_chip {
|
||||||
/*
|
/*
|
||||||
* Helper macros
|
* Helper macros
|
||||||
*/
|
*/
|
||||||
|
#define ONENAND_PAGES_PER_BLOCK (1<<6)
|
||||||
|
|
||||||
#define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index)
|
#define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index)
|
||||||
#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1)
|
#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1)
|
||||||
#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)
|
#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)
|
||||||
|
|
|
@ -131,6 +131,8 @@
|
||||||
#define ONENAND_CMD_LOCK_TIGHT (0x2C)
|
#define ONENAND_CMD_LOCK_TIGHT (0x2C)
|
||||||
#define ONENAND_CMD_UNLOCK_ALL (0x27)
|
#define ONENAND_CMD_UNLOCK_ALL (0x27)
|
||||||
#define ONENAND_CMD_ERASE (0x94)
|
#define ONENAND_CMD_ERASE (0x94)
|
||||||
|
#define ONENAND_CMD_MULTIBLOCK_ERASE (0x95)
|
||||||
|
#define ONENAND_CMD_ERASE_VERIFY (0x71)
|
||||||
#define ONENAND_CMD_RESET (0xF0)
|
#define ONENAND_CMD_RESET (0xF0)
|
||||||
#define ONENAND_CMD_OTP_ACCESS (0x65)
|
#define ONENAND_CMD_OTP_ACCESS (0x65)
|
||||||
#define ONENAND_CMD_READID (0x90)
|
#define ONENAND_CMD_READID (0x90)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/debug_locks.h>
|
#include <linux/debug_locks.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/kmsg_dump.h>
|
||||||
#include <linux/kallsyms.h>
|
#include <linux/kallsyms.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -74,6 +75,7 @@ NORET_TYPE void panic(const char * fmt, ...)
|
||||||
dump_stack();
|
dump_stack();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
kmsg_dump(KMSG_DUMP_PANIC);
|
||||||
/*
|
/*
|
||||||
* If we have crashed and we have a crash kernel loaded let it handle
|
* If we have crashed and we have a crash kernel loaded let it handle
|
||||||
* everything else.
|
* everything else.
|
||||||
|
@ -339,6 +341,7 @@ void oops_exit(void)
|
||||||
{
|
{
|
||||||
do_oops_enter_exit();
|
do_oops_enter_exit();
|
||||||
print_oops_end_marker();
|
print_oops_end_marker();
|
||||||
|
kmsg_dump(KMSG_DUMP_OOPS);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WANT_WARN_ON_SLOWPATH
|
#ifdef WANT_WARN_ON_SLOWPATH
|
||||||
|
|
119
kernel/printk.c
119
kernel/printk.c
|
@ -34,6 +34,7 @@
|
||||||
#include <linux/syscalls.h>
|
#include <linux/syscalls.h>
|
||||||
#include <linux/kexec.h>
|
#include <linux/kexec.h>
|
||||||
#include <linux/ratelimit.h>
|
#include <linux/ratelimit.h>
|
||||||
|
#include <linux/kmsg_dump.h>
|
||||||
|
|
||||||
#include <asm/uaccess.h>
|
#include <asm/uaccess.h>
|
||||||
|
|
||||||
|
@ -1405,4 +1406,122 @@ bool printk_timed_ratelimit(unsigned long *caller_jiffies,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(printk_timed_ratelimit);
|
EXPORT_SYMBOL(printk_timed_ratelimit);
|
||||||
|
|
||||||
|
static DEFINE_SPINLOCK(dump_list_lock);
|
||||||
|
static LIST_HEAD(dump_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kmsg_dump_register - register a kernel log dumper.
|
||||||
|
* @dump: pointer to the kmsg_dumper structure
|
||||||
|
*
|
||||||
|
* Adds a kernel log dumper to the system. The dump callback in the
|
||||||
|
* structure will be called when the kernel oopses or panics and must be
|
||||||
|
* set. Returns zero on success and %-EINVAL or %-EBUSY otherwise.
|
||||||
|
*/
|
||||||
|
int kmsg_dump_register(struct kmsg_dumper *dumper)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int err = -EBUSY;
|
||||||
|
|
||||||
|
/* The dump callback needs to be set */
|
||||||
|
if (!dumper->dump)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dump_list_lock, flags);
|
||||||
|
/* Don't allow registering multiple times */
|
||||||
|
if (!dumper->registered) {
|
||||||
|
dumper->registered = 1;
|
||||||
|
list_add_tail(&dumper->list, &dump_list);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dump_list_lock, flags);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kmsg_dump_register);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kmsg_dump_unregister - unregister a kmsg dumper.
|
||||||
|
* @dump: pointer to the kmsg_dumper structure
|
||||||
|
*
|
||||||
|
* Removes a dump device from the system. Returns zero on success and
|
||||||
|
* %-EINVAL otherwise.
|
||||||
|
*/
|
||||||
|
int kmsg_dump_unregister(struct kmsg_dumper *dumper)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
int err = -EINVAL;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dump_list_lock, flags);
|
||||||
|
if (dumper->registered) {
|
||||||
|
dumper->registered = 0;
|
||||||
|
list_del(&dumper->list);
|
||||||
|
err = 0;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&dump_list_lock, flags);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
|
||||||
|
|
||||||
|
static const char const *kmsg_reasons[] = {
|
||||||
|
[KMSG_DUMP_OOPS] = "oops",
|
||||||
|
[KMSG_DUMP_PANIC] = "panic",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *kmsg_to_str(enum kmsg_dump_reason reason)
|
||||||
|
{
|
||||||
|
if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
|
||||||
|
return "unknown";
|
||||||
|
|
||||||
|
return kmsg_reasons[reason];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kmsg_dump - dump kernel log to kernel message dumpers.
|
||||||
|
* @reason: the reason (oops, panic etc) for dumping
|
||||||
|
*
|
||||||
|
* Iterate through each of the dump devices and call the oops/panic
|
||||||
|
* callbacks with the log buffer.
|
||||||
|
*/
|
||||||
|
void kmsg_dump(enum kmsg_dump_reason reason)
|
||||||
|
{
|
||||||
|
unsigned long end;
|
||||||
|
unsigned chars;
|
||||||
|
struct kmsg_dumper *dumper;
|
||||||
|
const char *s1, *s2;
|
||||||
|
unsigned long l1, l2;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/* Theoretically, the log could move on after we do this, but
|
||||||
|
there's not a lot we can do about that. The new messages
|
||||||
|
will overwrite the start of what we dump. */
|
||||||
|
spin_lock_irqsave(&logbuf_lock, flags);
|
||||||
|
end = log_end & LOG_BUF_MASK;
|
||||||
|
chars = logged_chars;
|
||||||
|
spin_unlock_irqrestore(&logbuf_lock, flags);
|
||||||
|
|
||||||
|
if (logged_chars > end) {
|
||||||
|
s1 = log_buf + log_buf_len - logged_chars + end;
|
||||||
|
l1 = logged_chars - end;
|
||||||
|
|
||||||
|
s2 = log_buf;
|
||||||
|
l2 = end;
|
||||||
|
} else {
|
||||||
|
s1 = "";
|
||||||
|
l1 = 0;
|
||||||
|
|
||||||
|
s2 = log_buf + end - logged_chars;
|
||||||
|
l2 = logged_chars;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spin_trylock_irqsave(&dump_list_lock, flags)) {
|
||||||
|
printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n",
|
||||||
|
kmsg_to_str(reason));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list_for_each_entry(dumper, &dump_list, list)
|
||||||
|
dumper->dump(dumper, reason, s1, l1, s2, l2);
|
||||||
|
spin_unlock_irqrestore(&dump_list_lock, flags);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue