Merge git://git.infradead.org/mtd-2.6

* git://git.infradead.org/mtd-2.6: (82 commits)
  mtd: fix build error in m25p80.c
  mtd: Remove redundant mutex from mtd_blkdevs.c
  MTD: Fix wrong check register_blkdev return value
  Revert "mtd: cleanup Kconfig dependencies"
  mtd: cfi_cmdset_0002: make sector erase command variable
  mtd: cfi_cmdset_0002: add CFI detection for SST 38VF640x chips
  mtd: cfi_util: add support for switching SST 39VF640xB chips into QRY mode
  mtd: cfi_cmdset_0001: use defined value of P_ID_INTEL_PERFORMANCE instead of hardcoded one
  block2mtd: dubious assignment
  P4080/mtd: Fix the freescale lbc issue with 36bit mode
  P4080/eLBC: Make Freescale elbc interrupt common to elbc devices
  mtd: phram: use KBUILD_MODNAME
  mtd: OneNAND: S5PC110: Fix double call suspend & resume function
  mtd: nand: fix MTD_MODE_RAW writes
  jffs2: use kmemdup
  mtd: sm_ftl: cosmetic, use bool when possible
  mtd: r852: remove useless pci powerup/down from suspend/resume routines
  mtd: blktrans: fix a race vs kthread_stop
  mtd: blktrans: kill BKL
  mtd: allow to unload the mtdtrans module if its block devices aren't open
  ...

Fix up trivial whitespace-introduced conflict in drivers/mtd/mtdchar.c
This commit is contained in:
Linus Torvalds 2010-10-30 08:31:35 -07:00
commit 79346507ad
71 changed files with 3404 additions and 1151 deletions

View File

@ -66,7 +66,7 @@ static DEFINE_SPINLOCK(syscon_resetreg_lock);
* AMBA bus * AMBA bus
* | * |
* +- CPU * +- CPU
* +- NANDIF NAND Flash interface * +- FSMC NANDIF NAND Flash interface
* +- SEMI Shared Memory interface * +- SEMI Shared Memory interface
* +- ISP Image Signal Processor (U335 only) * +- ISP Image Signal Processor (U335 only)
* +- CDS (U335 only) * +- CDS (U335 only)
@ -726,7 +726,7 @@ static struct clk cpu_clk = {
}; };
static struct clk nandif_clk = { static struct clk nandif_clk = {
.name = "NANDIF", .name = "FSMC",
.parent = &amba_clk, .parent = &amba_clk,
.hw_ctrld = false, .hw_ctrld = false,
.reset = true, .reset = true,
@ -1259,7 +1259,7 @@ static struct clk_lookup lookups[] = {
/* Connected directly to the AMBA bus */ /* Connected directly to the AMBA bus */
DEF_LOOKUP("amba", &amba_clk), DEF_LOOKUP("amba", &amba_clk),
DEF_LOOKUP("cpu", &cpu_clk), DEF_LOOKUP("cpu", &cpu_clk),
DEF_LOOKUP("fsmc", &nandif_clk), DEF_LOOKUP("fsmc-nand", &nandif_clk),
DEF_LOOKUP("semi", &semi_clk), DEF_LOOKUP("semi", &semi_clk),
#ifdef CONFIG_MACH_U300_BS335 #ifdef CONFIG_MACH_U300_BS335
DEF_LOOKUP("isp", &isp_clk), DEF_LOOKUP("isp", &isp_clk),

View File

@ -21,7 +21,8 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <mach/coh901318.h> #include <linux/mtd/nand.h>
#include <linux/mtd/fsmc.h>
#include <asm/types.h> #include <asm/types.h>
#include <asm/setup.h> #include <asm/setup.h>
@ -30,6 +31,7 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <asm/mach/irq.h> #include <asm/mach/irq.h>
#include <mach/coh901318.h>
#include <mach/hardware.h> #include <mach/hardware.h>
#include <mach/syscon.h> #include <mach/syscon.h>
#include <mach/dma_channels.h> #include <mach/dma_channels.h>
@ -285,6 +287,13 @@ static struct resource rtc_resources[] = {
*/ */
static struct resource fsmc_resources[] = { static struct resource fsmc_resources[] = {
{ {
.name = "nand_data",
.start = U300_NAND_CS0_PHYS_BASE,
.end = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "fsmc_regs",
.start = U300_NAND_IF_PHYS_BASE, .start = U300_NAND_IF_PHYS_BASE,
.end = U300_NAND_IF_PHYS_BASE + SZ_4K - 1, .end = U300_NAND_IF_PHYS_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM, .flags = IORESOURCE_MEM,
@ -1429,11 +1438,39 @@ static struct platform_device rtc_device = {
.resource = rtc_resources, .resource = rtc_resources,
}; };
static struct platform_device fsmc_device = { static struct mtd_partition u300_partitions[] = {
.name = "nandif", {
.name = "bootrecords",
.offset = 0,
.size = SZ_128K,
},
{
.name = "free",
.offset = SZ_128K,
.size = 8064 * SZ_1K,
},
{
.name = "platform",
.offset = 8192 * SZ_1K,
.size = 253952 * SZ_1K,
},
};
static struct fsmc_nand_platform_data nand_platform_data = {
.partitions = u300_partitions,
.nr_partitions = ARRAY_SIZE(u300_partitions),
.options = NAND_SKIP_BBTSCAN,
.width = FSMC_NAND_BW8,
};
static struct platform_device nand_device = {
.name = "fsmc-nand",
.id = -1, .id = -1,
.num_resources = ARRAY_SIZE(fsmc_resources),
.resource = fsmc_resources, .resource = fsmc_resources,
.num_resources = ARRAY_SIZE(fsmc_resources),
.dev = {
.platform_data = &nand_platform_data,
},
}; };
static struct platform_device ave_device = { static struct platform_device ave_device = {
@ -1465,7 +1502,7 @@ static struct platform_device *platform_devs[] __initdata = {
&keypad_device, &keypad_device,
&rtc_device, &rtc_device,
&gpio_device, &gpio_device,
&fsmc_device, &nand_device,
&wdog_device, &wdog_device,
&ave_device &ave_device
}; };

View File

@ -20,11 +20,9 @@
/* NAND Flash CS0 */ /* NAND Flash CS0 */
#define U300_NAND_CS0_PHYS_BASE 0x80000000 #define U300_NAND_CS0_PHYS_BASE 0x80000000
#define U300_NAND_CS0_VIRT_BASE 0xff040000
/* NFIF */ /* NFIF */
#define U300_NAND_IF_PHYS_BASE 0x9f800000 #define U300_NAND_IF_PHYS_BASE 0x9f800000
#define U300_NAND_IF_VIRT_BASE 0xff030000
/* AHB Peripherals */ /* AHB Peripherals */
#define U300_AHB_PER_PHYS_BASE 0xa0000000 #define U300_AHB_PER_PHYS_BASE 0xa0000000

View File

@ -30,15 +30,15 @@ struct pxa3xx_nand_cmdset {
}; };
struct pxa3xx_nand_flash { struct pxa3xx_nand_flash {
const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ uint32_t chip_id;
const struct pxa3xx_nand_cmdset *cmdset; unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */
unsigned int page_size; /* Page size in bytes (PAGE_SZ) */
unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
unsigned int num_blocks; /* Number of physical blocks in Flash */
uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */ struct pxa3xx_nand_cmdset *cmdset; /* NAND command set */
uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */
uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */
uint32_t num_blocks; /* Number of physical blocks in Flash */
uint32_t chip_id;
}; };
struct pxa3xx_nand_platform_data { struct pxa3xx_nand_platform_data {

View File

@ -0,0 +1,97 @@
#ifndef __BCM963XX_TAG_H
#define __BCM963XX_TAG_H
#define TAGVER_LEN 4 /* Length of Tag Version */
#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */
#define SIG1_LEN 20 /* Company Signature 1 Length */
#define SIG2_LEN 14 /* Company Signature 2 Lenght */
#define BOARDID_LEN 16 /* Length of BoardId */
#define ENDIANFLAG_LEN 2 /* Endian Flag Length */
#define CHIPID_LEN 6 /* Chip Id Length */
#define IMAGE_LEN 10 /* Length of Length Field */
#define ADDRESS_LEN 12 /* Length of Address field */
#define DUALFLAG_LEN 2 /* Dual Image flag Length */
#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */
#define RSASIG_LEN 20 /* Length of RSA Signature in tag */
#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */
#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */
#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */
#define CRC_LEN 4 /* Length of CRC in bytes */
#define ALTTAGINFO_LEN 54 /* Alternate length for vendor information; Pirelli */
#define NUM_PIRELLI 2
#define IMAGETAG_CRC_START 0xFFFFFFFF
#define PIRELLI_BOARDS { \
"AGPF-S0", \
"DWV-S0", \
}
/*
* The broadcom firmware assumes the rootfs starts the image,
* therefore uses the rootfs start (flash_image_address)
* to determine where to flash the image. Since we have the kernel first
* we have to give it the kernel address, but the crc uses the length
* associated with this address (root_length), which is added to the kernel
* length (kernel_length) to determine the length of image to flash and thus
* needs to be rootfs + deadcode (jffs2 EOF marker)
*/
struct bcm_tag {
/* 0-3: Version of the image tag */
char tag_version[TAGVER_LEN];
/* 4-23: Company Line 1 */
char sig_1[SIG1_LEN];
/* 24-37: Company Line 2 */
char sig_2[SIG2_LEN];
/* 38-43: Chip this image is for */
char chip_id[CHIPID_LEN];
/* 44-59: Board name */
char board_id[BOARDID_LEN];
/* 60-61: Map endianness -- 1 BE 0 LE */
char big_endian[ENDIANFLAG_LEN];
/* 62-71: Total length of image */
char total_length[IMAGE_LEN];
/* 72-83: Address in memory of CFE */
char cfe__address[ADDRESS_LEN];
/* 84-93: Size of CFE */
char cfe_length[IMAGE_LEN];
/* 94-105: Address in memory of image start
* (kernel for OpenWRT, rootfs for stock firmware)
*/
char flash_image_start[ADDRESS_LEN];
/* 106-115: Size of rootfs */
char root_length[IMAGE_LEN];
/* 116-127: Address in memory of kernel */
char kernel_address[ADDRESS_LEN];
/* 128-137: Size of kernel */
char kernel_length[IMAGE_LEN];
/* 138-139: Unused at the moment */
char dual_image[DUALFLAG_LEN];
/* 140-141: Unused at the moment */
char inactive_flag[INACTIVEFLAG_LEN];
/* 142-161: RSA Signature (not used; some vendors may use this) */
char rsa_signature[RSASIG_LEN];
/* 162-191: Compilation and related information (not used in OpenWrt) */
char information1[TAGINFO1_LEN];
/* 192-195: Version flash layout */
char flash_layout_ver[FLASHLAYOUTVER_LEN];
/* 196-199: kernel+rootfs CRC32 */
char fskernel_crc[CRC_LEN];
/* 200-215: Unused except on Alice Gate where is is information */
char information2[TAGINFO2_LEN];
/* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */
char image_crc[CRC_LEN];
/* 220-223: CRC32 of rootfs partition */
char rootfs_crc[CRC_LEN];
/* 224-227: CRC32 of kernel partition */
char kernel_crc[CRC_LEN];
/* 228-235: Unused at present */
char reserved1[8];
/* 236-239: CRC32 of header excluding tagVersion */
char header_crc[CRC_LEN];
/* 240-255: Unused at present */
char reserved2[16];
};
#endif /* __BCM63XX_TAG_H */

View File

@ -682,9 +682,12 @@ config 4xx_SOC
bool bool
config FSL_LBC config FSL_LBC
bool bool "Freescale Local Bus support"
depends on FSL_SOC
help help
Freescale Localbus support Enables reporting of errors from the Freescale local bus
controller. Also contains some common code used by
drivers for specific local bus peripherals.
config FSL_GTM config FSL_GTM
bool bool

View File

@ -1,9 +1,10 @@
/* Freescale Local Bus Controller /* Freescale Local Bus Controller
* *
* Copyright (c) 2006-2007 Freescale Semiconductor * Copyright © 2006-2007, 2010 Freescale Semiconductor
* *
* Authors: Nick Spence <nick.spence@freescale.com>, * Authors: Nick Spence <nick.spence@freescale.com>,
* Scott Wood <scottwood@freescale.com> * Scott Wood <scottwood@freescale.com>
* Jack Lan <jack.lan@freescale.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -26,6 +27,8 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/device.h>
#include <linux/spinlock.h>
struct fsl_lbc_bank { struct fsl_lbc_bank {
__be32 br; /**< Base Register */ __be32 br; /**< Base Register */
@ -125,13 +128,23 @@ struct fsl_lbc_regs {
#define LTESR_ATMW 0x00800000 #define LTESR_ATMW 0x00800000
#define LTESR_ATMR 0x00400000 #define LTESR_ATMR 0x00400000
#define LTESR_CS 0x00080000 #define LTESR_CS 0x00080000
#define LTESR_UPM 0x00000002
#define LTESR_CC 0x00000001 #define LTESR_CC 0x00000001
#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
#define LTESR_MASK (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
| LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
| LTESR_CC)
#define LTESR_CLEAR 0xFFFFFFFF
#define LTECCR_CLEAR 0xFFFFFFFF
#define LTESR_STATUS LTESR_MASK
#define LTEIR_ENABLE LTESR_MASK
#define LTEDR_ENABLE 0x00000000
__be32 ltedr; /**< Transfer Error Disable Register */ __be32 ltedr; /**< Transfer Error Disable Register */
__be32 lteir; /**< Transfer Error Interrupt Register */ __be32 lteir; /**< Transfer Error Interrupt Register */
__be32 lteatr; /**< Transfer Error Attributes Register */ __be32 lteatr; /**< Transfer Error Attributes Register */
__be32 ltear; /**< Transfer Error Address Register */ __be32 ltear; /**< Transfer Error Address Register */
u8 res6[0xC]; __be32 lteccr; /**< Transfer Error ECC Register */
u8 res6[0x8];
__be32 lbcr; /**< Configuration Register */ __be32 lbcr; /**< Configuration Register */
#define LBCR_LDIS 0x80000000 #define LBCR_LDIS 0x80000000
#define LBCR_LDIS_SHIFT 31 #define LBCR_LDIS_SHIFT 31
@ -235,6 +248,7 @@ struct fsl_upm {
int width; int width;
}; };
extern u32 fsl_lbc_addr(phys_addr_t addr_base);
extern int fsl_lbc_find(phys_addr_t addr_base); extern int fsl_lbc_find(phys_addr_t addr_base);
extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm); extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm);
@ -265,7 +279,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
cpu_relax(); cpu_relax();
} }
/* overview of the fsl lbc controller */
struct fsl_lbc_ctrl {
/* device info */
struct device *dev;
struct fsl_lbc_regs __iomem *regs;
int irq;
wait_queue_head_t irq_wait;
spinlock_t lock;
void *nand;
/* status read from LTESR by irq handler */
unsigned int irq_status;
};
extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
u32 mar); u32 mar);
extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
#endif /* __ASM_FSL_LBC_H */ #endif /* __ASM_FSL_LBC_H */

View File

@ -1,9 +1,12 @@
/* /*
* Freescale LBC and UPM routines. * Freescale LBC and UPM routines.
* *
* Copyright (c) 2007-2008 MontaVista Software, Inc. * Copyright © 2007-2008 MontaVista Software, Inc.
* Copyright © 2010 Freescale Semiconductor
* *
* Author: Anton Vorontsov <avorontsov@ru.mvista.com> * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
* Author: Jack Lan <Jack.Lan@freescale.com>
* Author: Roy Zang <tie-fei.zang@freescale.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -19,39 +22,37 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/fsl_lbc.h> #include <asm/fsl_lbc.h>
static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock); static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
static struct fsl_lbc_regs __iomem *fsl_lbc_regs; struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
static char __initdata *compat_lbc[] = { /**
"fsl,pq2-localbus", * fsl_lbc_addr - convert the base address
"fsl,pq2pro-localbus", * @addr_base: base address of the memory bank
"fsl,pq3-localbus", *
"fsl,elbc", * This function converts a base address of lbc into the right format for the
}; * BR register. If the SOC has eLBC then it returns 32bit physical address
* else it convers a 34bit local bus physical address to correct format of
static int __init fsl_lbc_init(void) * 32bit address for BR register (Example: MPC8641).
*/
u32 fsl_lbc_addr(phys_addr_t addr_base)
{ {
struct device_node *lbus; struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
int i; u32 addr = addr_base & 0xffff8000;
for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) { if (of_device_is_compatible(np, "fsl,elbc"))
lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]); return addr;
if (lbus)
goto found;
}
return -ENODEV;
found: return addr | ((addr_base & 0x300000000ull) >> 19);
fsl_lbc_regs = of_iomap(lbus, 0);
of_node_put(lbus);
if (!fsl_lbc_regs)
return -ENOMEM;
return 0;
} }
arch_initcall(fsl_lbc_init); EXPORT_SYMBOL(fsl_lbc_addr);
/** /**
* fsl_lbc_find - find Localbus bank * fsl_lbc_find - find Localbus bank
@ -65,15 +66,17 @@ arch_initcall(fsl_lbc_init);
int fsl_lbc_find(phys_addr_t addr_base) int fsl_lbc_find(phys_addr_t addr_base)
{ {
int i; int i;
struct fsl_lbc_regs __iomem *lbc;
if (!fsl_lbc_regs) if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV; return -ENODEV;
for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) { lbc = fsl_lbc_ctrl_dev->regs;
__be32 br = in_be32(&fsl_lbc_regs->bank[i].br); for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
__be32 or = in_be32(&fsl_lbc_regs->bank[i].or); __be32 br = in_be32(&lbc->bank[i].br);
__be32 or = in_be32(&lbc->bank[i].or);
if (br & BR_V && (br & or & BR_BA) == addr_base) if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
return i; return i;
} }
@ -94,22 +97,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
{ {
int bank; int bank;
__be32 br; __be32 br;
struct fsl_lbc_regs __iomem *lbc;
bank = fsl_lbc_find(addr_base); bank = fsl_lbc_find(addr_base);
if (bank < 0) if (bank < 0)
return bank; return bank;
br = in_be32(&fsl_lbc_regs->bank[bank].br); if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
lbc = fsl_lbc_ctrl_dev->regs;
br = in_be32(&lbc->bank[bank].br);
switch (br & BR_MSEL) { switch (br & BR_MSEL) {
case BR_MS_UPMA: case BR_MS_UPMA:
upm->mxmr = &fsl_lbc_regs->mamr; upm->mxmr = &lbc->mamr;
break; break;
case BR_MS_UPMB: case BR_MS_UPMB:
upm->mxmr = &fsl_lbc_regs->mbmr; upm->mxmr = &lbc->mbmr;
break; break;
case BR_MS_UPMC: case BR_MS_UPMC:
upm->mxmr = &fsl_lbc_regs->mcmr; upm->mxmr = &lbc->mcmr;
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -148,9 +156,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
int ret = 0; int ret = 0;
unsigned long flags; unsigned long flags;
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
spin_lock_irqsave(&fsl_lbc_lock, flags); spin_lock_irqsave(&fsl_lbc_lock, flags);
out_be32(&fsl_lbc_regs->mar, mar); out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
switch (upm->width) { switch (upm->width) {
case 8: case 8:
@ -172,3 +183,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
return ret; return ret;
} }
EXPORT_SYMBOL(fsl_upm_run_pattern); EXPORT_SYMBOL(fsl_upm_run_pattern);
static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
{
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
/* clear event registers */
setbits32(&lbc->ltesr, LTESR_CLEAR);
out_be32(&lbc->lteatr, 0);
out_be32(&lbc->ltear, 0);
out_be32(&lbc->lteccr, LTECCR_CLEAR);
out_be32(&lbc->ltedr, LTEDR_ENABLE);
/* Enable interrupts for any detected events */
out_be32(&lbc->lteir, LTEIR_ENABLE);
return 0;
}
/*
* NOTE: This interrupt is used to report localbus events of various kinds,
* such as transaction errors on the chipselects.
*/
static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
{
struct fsl_lbc_ctrl *ctrl = data;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
u32 status;
status = in_be32(&lbc->ltesr);
if (!status)
return IRQ_NONE;
out_be32(&lbc->ltesr, LTESR_CLEAR);
out_be32(&lbc->lteatr, 0);
out_be32(&lbc->ltear, 0);
ctrl->irq_status = status;
if (status & LTESR_BM)
dev_err(ctrl->dev, "Local bus monitor time-out: "
"LTESR 0x%08X\n", status);
if (status & LTESR_WP)
dev_err(ctrl->dev, "Write protect error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_ATMW)
dev_err(ctrl->dev, "Atomic write error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_ATMR)
dev_err(ctrl->dev, "Atomic read error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_CS)
dev_err(ctrl->dev, "Chip select error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_UPM)
;
if (status & LTESR_FCT) {
dev_err(ctrl->dev, "FCM command time-out: "
"LTESR 0x%08X\n", status);
smp_wmb();
wake_up(&ctrl->irq_wait);
}
if (status & LTESR_PAR) {
dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
"LTESR 0x%08X\n", status);
smp_wmb();
wake_up(&ctrl->irq_wait);
}
if (status & LTESR_CC) {
smp_wmb();
wake_up(&ctrl->irq_wait);
}
if (status & ~LTESR_MASK)
dev_err(ctrl->dev, "Unknown error: "
"LTESR 0x%08X\n", status);
return IRQ_HANDLED;
}
/*
* fsl_lbc_ctrl_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code allocates all of
* the resources needed for the controller only. The
* resources for the NAND banks themselves are allocated
* in the chip probe function.
*/
static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
{
int ret;
if (!dev->dev.of_node) {
dev_err(&dev->dev, "Device OF-Node is NULL");
return -EFAULT;
}
fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
if (!fsl_lbc_ctrl_dev)
return -ENOMEM;
dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
spin_lock_init(&fsl_lbc_ctrl_dev->lock);
init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
if (!fsl_lbc_ctrl_dev->regs) {
dev_err(&dev->dev, "failed to get memory region\n");
ret = -ENODEV;
goto err;
}
fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_lbc_ctrl_dev->irq == NO_IRQ) {
dev_err(&dev->dev, "failed to get irq resource\n");
ret = -ENODEV;
goto err;
}
fsl_lbc_ctrl_dev->dev = &dev->dev;
ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
if (ret < 0)
goto err;
ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0,
"fsl-lbc", fsl_lbc_ctrl_dev);
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_lbc_ctrl_dev->irq);
ret = fsl_lbc_ctrl_dev->irq;
goto err;
}
return 0;
err:
iounmap(fsl_lbc_ctrl_dev->regs);
kfree(fsl_lbc_ctrl_dev);
return ret;
}
static const struct of_device_id fsl_lbc_match[] = {
{ .compatible = "fsl,elbc", },
{ .compatible = "fsl,pq3-localbus", },
{ .compatible = "fsl,pq2-localbus", },
{ .compatible = "fsl,pq2pro-localbus", },
{},
};
static struct platform_driver fsl_lbc_ctrl_driver = {
.driver = {
.name = "fsl-lbc",
.of_match_table = fsl_lbc_match,
},
.probe = fsl_lbc_ctrl_probe,
};
static int __init fsl_lbc_init(void)
{
return platform_driver_register(&fsl_lbc_ctrl_driver);
}
module_init(fsl_lbc_init);

View File

@ -1496,7 +1496,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
switch (mode) { switch (mode) {
case FL_WRITING: case FL_WRITING:
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41); write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0x40) : CMD(0x41);
break; break;
case FL_OTP_WRITE: case FL_OTP_WRITE:
write_cmd = CMD(0xc0); write_cmd = CMD(0xc0);
@ -1661,7 +1661,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
cmd_adr = adr & ~(wbufsize-1); cmd_adr = adr & ~(wbufsize-1);
/* Let's determine this according to the interleave only once */ /* Let's determine this according to the interleave only once */
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9); write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
ret = get_chip(map, chip, cmd_adr, FL_WRITING); ret = get_chip(map, chip, cmd_adr, FL_WRITING);

View File

@ -291,6 +291,23 @@ static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param)
cfi->addr_unlock1 = 0x555; cfi->addr_unlock1 = 0x555;
cfi->addr_unlock2 = 0x2AA; cfi->addr_unlock2 = 0x2AA;
cfi->sector_erase_cmd = CMD(0x50);
}
static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
fixup_sst39vf_rev_b(mtd, param);
/*
* CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where
* it should report a size of 8KBytes (0x0020*256).
*/
cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
pr_warning("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", mtd->name);
} }
static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param)
@ -317,14 +334,14 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
/* Used to fix CFI-Tables of chips without Extended Query Tables */ /* Used to fix CFI-Tables of chips without Extended Query Tables */
static struct cfi_fixup cfi_nopri_fixup_table[] = { static struct cfi_fixup cfi_nopri_fixup_table[] = {
{ CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602 { CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, /* SST39VF1602 */
{ CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601 { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, /* SST39VF1601 */
{ CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202 { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, /* SST39VF3202 */
{ CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201 { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, /* SST39VF3201 */
{ CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B { CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3202B */
{ CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B { CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3201B */
{ CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B { CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6402B */
{ CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B { CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6401B */
{ 0, 0, NULL, NULL } { 0, 0, NULL, NULL }
}; };
@ -344,6 +361,10 @@ static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_SST, 0x536A, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6402 */
{ CFI_MFR_SST, 0x536B, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6401 */
{ CFI_MFR_SST, 0x536C, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6404 */
{ CFI_MFR_SST, 0x536D, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6403 */
#if !FORCE_WORD_WRITE #if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif #endif
@ -374,6 +395,13 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e && if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e &&
extp->MajorVersion == '0') extp->MajorVersion == '0')
extp->MajorVersion = '1'; extp->MajorVersion = '1';
/*
* SST 38VF640x chips report major=0xFF / minor=0xFF.
*/
if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) {
extp->MajorVersion = '1';
extp->MinorVersion = '0';
}
} }
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
@ -545,15 +573,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
goto setup_err; goto setup_err;
} }
#if 0
// debug
for (i=0; i<mtd->numeraseregions;i++){
printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
i,mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].numblocks);
}
#endif
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
register_reboot_notifier(&mtd->reboot_notifier); register_reboot_notifier(&mtd->reboot_notifier);
@ -674,7 +693,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
* there was an error (so leave the erase * there was an error (so leave the erase
* routine to recover from it) or we trying to * routine to recover from it) or we trying to
* use the erase-in-progress sector. */ * use the erase-in-progress sector. */
map_write(map, CMD(0x30), chip->in_progress_block_addr); map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->oldstate = FL_READY; chip->oldstate = FL_READY;
printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
@ -727,7 +746,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
switch(chip->oldstate) { switch(chip->oldstate) {
case FL_ERASING: case FL_ERASING:
chip->state = chip->oldstate; chip->state = chip->oldstate;
map_write(map, CMD(0x30), chip->in_progress_block_addr); map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
chip->oldstate = FL_READY; chip->oldstate = FL_READY;
chip->state = FL_ERASING; chip->state = FL_ERASING;
break; break;
@ -870,7 +889,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
local_irq_disable(); local_irq_disable();
/* Resume the write or erase operation */ /* Resume the write or erase operation */
map_write(map, CMD(0x30), adr); map_write(map, cfi->sector_erase_cmd, adr);
chip->state = oldstate; chip->state = oldstate;
start = xip_currtime(); start = xip_currtime();
} else if (usec >= 1000000/HZ) { } else if (usec >= 1000000/HZ) {
@ -1025,9 +1044,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
if (chip->state != FL_READY){ if (chip->state != FL_READY){
#if 0
printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
#endif
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
@ -1035,10 +1051,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
#if 0
if(signal_pending(current))
return -EINTR;
#endif
timeo = jiffies + HZ; timeo = jiffies + HZ;
goto retry; goto retry;
@ -1246,9 +1258,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
mutex_lock(&cfi->chips[chipnum].mutex); mutex_lock(&cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) { if (cfi->chips[chipnum].state != FL_READY) {
#if 0
printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
#endif
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait); add_wait_queue(&cfi->chips[chipnum].wq, &wait);
@ -1256,10 +1265,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
schedule(); schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait); remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
#if 0
if(signal_pending(current))
return -EINTR;
#endif
goto retry; goto retry;
} }
@ -1324,9 +1329,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
mutex_lock(&cfi->chips[chipnum].mutex); mutex_lock(&cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) { if (cfi->chips[chipnum].state != FL_READY) {
#if 0
printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
#endif
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait); add_wait_queue(&cfi->chips[chipnum].wq, &wait);
@ -1334,10 +1336,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
schedule(); schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait); remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
#if 0
if(signal_pending(current))
return -EINTR;
#endif
goto retry1; goto retry1;
} }
@ -1396,7 +1394,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
/* Write Buffer Load */ /* Write Buffer Load */
map_write(map, CMD(0x25), cmd_adr); map_write(map, CMD(0x25), cmd_adr);
@ -1675,7 +1672,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
map_write(map, CMD(0x30), adr); map_write(map, cfi->sector_erase_cmd, adr);
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;

View File

@ -177,6 +177,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
cfi->cfi_mode = CFI_MODE_CFI; cfi->cfi_mode = CFI_MODE_CFI;
cfi->sector_erase_cmd = CMD(0x30);
/* Read the CFI info structure */ /* Read the CFI info structure */
xip_disable_qry(base, map, cfi); xip_disable_qry(base, map, cfi);
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)

View File

@ -75,6 +75,13 @@ int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(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(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
if (cfi_qry_present(map, base, cfi))
return 1;
/* SST 39VF640xB */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
if (cfi_qry_present(map, base, cfi)) if (cfi_qry_present(map, base, cfi))
return 1; return 1;
/* QRY not found */ /* QRY not found */

View File

@ -91,7 +91,6 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
} else } else
instr->state = MTD_ERASE_DONE; instr->state = MTD_ERASE_DONE;
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr); mtd_erase_callback(instr);
return err; return err;
} }

View File

@ -661,11 +661,14 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) }, { "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SECT_4K) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) }, { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) }, { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */ /* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) }, { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) },
@ -714,6 +717,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) }, { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
/* Catalyst / On Semiconductor -- non-JEDEC */ /* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, { "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
@ -924,7 +928,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
nr_parts = data->nr_parts; nr_parts = data->nr_parts;
} }
#ifdef CONFIG_OF #ifdef CONFIG_MTD_OF_PARTS
if (nr_parts <= 0 && spi->dev.of_node) { if (nr_parts <= 0 && spi->dev.of_node) {
nr_parts = of_mtd_parse_partitions(&spi->dev, nr_parts = of_mtd_parse_partitions(&spi->dev,
spi->dev.of_node, &parts); spi->dev.of_node, &parts);

View File

@ -15,7 +15,7 @@
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
*/ */
#define pr_fmt(fmt) "phram: " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/io.h> #include <asm/io.h>
#include <linux/init.h> #include <linux/init.h>

View File

@ -251,6 +251,15 @@ config MTD_NETtel
help help
Support for flash chips on NETtel/SecureEdge/SnapGear boards. Support for flash chips on NETtel/SecureEdge/SnapGear boards.
config MTD_BCM963XX
tristate "Map driver for Broadcom BCM963xx boards"
depends on BCM63XX
select MTD_MAP_BANK_WIDTH_2
select MTD_CFI_I1
help
Support for parsing CFE image tag and creating MTD partitions on
Broadcom BCM63xx boards.
config MTD_DILNETPC config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC" tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN

View File

@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o

View File

@ -0,0 +1,271 @@
/*
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
* Mike Albon <malbon@openwrt.org>
* Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
#define BCM63XX_BUSWIDTH 2 /* Buswidth */
#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
#define PFX KBUILD_MODNAME ": "
static struct mtd_partition *parsed_parts;
static struct mtd_info *bcm963xx_mtd_info;
static struct map_info bcm963xx_map = {
.name = "bcm963xx",
.bankwidth = BCM63XX_BUSWIDTH,
};
static int parse_cfe_partitions(struct mtd_info *master,
struct mtd_partition **pparts)
{
/* CFE, NVRAM and global Linux are always present */
int nrparts = 3, curpart = 0;
struct bcm_tag *buf;
struct mtd_partition *parts;
int ret;
size_t retlen;
unsigned int rootfsaddr, kerneladdr, spareaddr;
unsigned int rootfslen, kernellen, sparelen, totallen;
int namelen = 0;
int i;
char *boardid;
char *tagversion;
/* Allocate memory for buffer */
buf = vmalloc(sizeof(struct bcm_tag));
if (!buf)
return -ENOMEM;
/* Get the tag */
ret = master->read(master, master->erasesize, sizeof(struct bcm_tag),
&retlen, (void *)buf);
if (retlen != sizeof(struct bcm_tag)) {
vfree(buf);
return -EIO;
}
sscanf(buf->kernel_address, "%u", &kerneladdr);
sscanf(buf->kernel_length, "%u", &kernellen);
sscanf(buf->total_length, "%u", &totallen);
tagversion = &(buf->tag_version[0]);
boardid = &(buf->board_id[0]);
printk(KERN_INFO PFX "CFE boot tag found with version %s "
"and board type %s\n", tagversion, boardid);
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
rootfsaddr = kerneladdr + kernellen;
spareaddr = roundup(totallen, master->erasesize) + master->erasesize;
sparelen = master->size - spareaddr - master->erasesize;
rootfslen = spareaddr - rootfsaddr;
/* Determine number of partitions */
namelen = 8;
if (rootfslen > 0) {
nrparts++;
namelen += 6;
};
if (kernellen > 0) {
nrparts++;
namelen += 6;
};
/* Ask kernel for more memory */
parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
if (!parts) {
vfree(buf);
return -ENOMEM;
};
/* Start building partition list */
parts[curpart].name = "CFE";
parts[curpart].offset = 0;
parts[curpart].size = master->erasesize;
curpart++;
if (kernellen > 0) {
parts[curpart].name = "kernel";
parts[curpart].offset = kerneladdr;
parts[curpart].size = kernellen;
curpart++;
};
if (rootfslen > 0) {
parts[curpart].name = "rootfs";
parts[curpart].offset = rootfsaddr;
parts[curpart].size = rootfslen;
if (sparelen > 0)
parts[curpart].size += sparelen;
curpart++;
};
parts[curpart].name = "nvram";
parts[curpart].offset = master->size - master->erasesize;
parts[curpart].size = master->erasesize;
/* Global partition "linux" to make easy firmware upgrade */
curpart++;
parts[curpart].name = "linux";
parts[curpart].offset = parts[0].size;
parts[curpart].size = master->size - parts[0].size - parts[3].size;
for (i = 0; i < nrparts; i++)
printk(KERN_INFO PFX "Partition %d is %s offset %lx and "
"length %lx\n", i, parts[i].name,
(long unsigned int)(parts[i].offset),
(long unsigned int)(parts[i].size));
printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n",
spareaddr, sparelen);
*pparts = parts;
vfree(buf);
return nrparts;
};
static int bcm963xx_detect_cfe(struct mtd_info *master)
{
int idoffset = 0x4e0;
static char idstring[8] = "CFE1CFE1";
char buf[9];
int ret;
size_t retlen;
ret = master->read(master, idoffset, 8, &retlen, (void *)buf);
buf[retlen] = 0;
printk(KERN_INFO PFX "Read Signature value of %s\n", buf);
return strncmp(idstring, buf, 8);
}
static int bcm963xx_probe(struct platform_device *pdev)
{
int err = 0;
int parsed_nr_parts = 0;
char *part_type;
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "no resource supplied\n");
return -ENODEV;
}
bcm963xx_map.phys = r->start;
bcm963xx_map.size = resource_size(r);
bcm963xx_map.virt = ioremap(r->start, resource_size(r));
if (!bcm963xx_map.virt) {
dev_err(&pdev->dev, "failed to ioremap\n");
return -EIO;
}
dev_info(&pdev->dev, "0x%08lx at 0x%08x\n",
bcm963xx_map.size, bcm963xx_map.phys);
simple_map_init(&bcm963xx_map);
bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map);
if (!bcm963xx_mtd_info) {
dev_err(&pdev->dev, "failed to probe using CFI\n");
err = -EIO;
goto err_probe;
}
bcm963xx_mtd_info->owner = THIS_MODULE;
/* This is mutually exclusive */
if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) {
dev_info(&pdev->dev, "CFE bootloader detected\n");
if (parsed_nr_parts == 0) {
int ret = parse_cfe_partitions(bcm963xx_mtd_info,
&parsed_parts);
if (ret > 0) {
part_type = "CFE";
parsed_nr_parts = ret;
}
}
} else {
dev_info(&pdev->dev, "unsupported bootloader\n");
err = -ENODEV;
goto err_probe;
}
return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts,
parsed_nr_parts);
err_probe:
iounmap(bcm963xx_map.virt);
return err;
}
static int bcm963xx_remove(struct platform_device *pdev)
{
if (bcm963xx_mtd_info) {
del_mtd_partitions(bcm963xx_mtd_info);
map_destroy(bcm963xx_mtd_info);
}
if (bcm963xx_map.virt) {
iounmap(bcm963xx_map.virt);
bcm963xx_map.virt = 0;
}
return 0;
}
static struct platform_driver bcm63xx_mtd_dev = {
.probe = bcm963xx_probe,
.remove = bcm963xx_remove,
.driver = {
.name = "bcm963xx-flash",
.owner = THIS_MODULE,
},
};
static int __init bcm963xx_mtd_init(void)
{
return platform_driver_register(&bcm63xx_mtd_dev);
}
static void __exit bcm963xx_mtd_exit(void)
{
platform_driver_unregister(&bcm63xx_mtd_dev);
}
module_init(bcm963xx_mtd_init);
module_exit(bcm963xx_mtd_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot");
MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");

View File

@ -208,10 +208,14 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
if (!state) if (!state)
return -ENOMEM; return -ENOMEM;
/*
* We cast start/end to known types in the boards file, so cast
* away their pointer types here to the known types (gpios->xxx).
*/
state->gpio_count = gpios->end; state->gpio_count = gpios->end;
state->gpio_addrs = (void *)gpios->start; state->gpio_addrs = (void *)(unsigned long)gpios->start;
state->gpio_values = (void *)(state + 1); state->gpio_values = (void *)(state + 1);
state->win_size = memory->end - memory->start + 1; state->win_size = resource_size(memory);
memset(state->gpio_values, 0xff, arr_size); memset(state->gpio_values, 0xff, arr_size);
state->map.name = DRIVER_NAME; state->map.name = DRIVER_NAME;
@ -221,7 +225,7 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
state->map.copy_to = gf_copy_to; state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width; state->map.bankwidth = pdata->width;
state->map.size = state->win_size * (1 << state->gpio_count); state->map.size = state->win_size * (1 << state->gpio_count);
state->map.virt = (void __iomem *)memory->start; state->map.virt = ioremap_nocache(memory->start, state->map.size);
state->map.phys = NO_XIP; state->map.phys = NO_XIP;
state->map.map_priv_1 = (unsigned long)state; state->map.map_priv_1 = (unsigned long)state;

View File

@ -640,10 +640,6 @@ static int pcmciamtd_config(struct pcmcia_device *link)
} }
dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name); dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name);
return 0; return 0;
dev_err(&dev->p_dev->dev, "CS Error, exiting\n");
pcmciamtd_release(link);
return -ENODEV;
} }

View File

@ -50,7 +50,7 @@ static int parse_obsolete_partitions(struct platform_device *dev,
{ {
int i, plen, nr_parts; int i, plen, nr_parts;
const struct { const struct {
u32 offset, len; __be32 offset, len;
} *part; } *part;
const char *names; const char *names;
@ -69,9 +69,9 @@ static int parse_obsolete_partitions(struct platform_device *dev,
names = of_get_property(dp, "partition-names", &plen); names = of_get_property(dp, "partition-names", &plen);
for (i = 0; i < nr_parts; i++) { for (i = 0; i < nr_parts; i++) {
info->parts[i].offset = part->offset; info->parts[i].offset = be32_to_cpu(part->offset);
info->parts[i].size = part->len & ~1; info->parts[i].size = be32_to_cpu(part->len) & ~1;
if (part->len & 1) /* bit 0 set signifies read only partition */ if (be32_to_cpu(part->len) & 1) /* bit 0 set signifies read only partition */
info->parts[i].mask_flags = MTD_WRITEABLE; info->parts[i].mask_flags = MTD_WRITEABLE;
if (names && (plen > 0)) { if (names && (plen > 0)) {
@ -226,11 +226,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
struct resource res; struct resource res;
struct of_flash *info; struct of_flash *info;
const char *probe_type = match->data; const char *probe_type = match->data;
const u32 *width; const __be32 *width;
int err; int err;
int i; int i;
int count; int count;
const u32 *p; const __be32 *p;
int reg_tuple_size; int reg_tuple_size;
struct mtd_info **mtd_list = NULL; struct mtd_info **mtd_list = NULL;
resource_size_t res_size; resource_size_t res_size;
@ -267,9 +267,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
err = -ENXIO; err = -ENXIO;
if (of_address_to_resource(dp, i, &res)) { if (of_address_to_resource(dp, i, &res)) {
dev_err(&dev->dev, "Can't get IO address from device" /*
" tree\n"); * Continue with next register tuple if this
goto err_out; * one is not mappable
*/
continue;
} }
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",

View File

@ -37,7 +37,6 @@
#include "mtdcore.h" #include "mtdcore.h"
static DEFINE_MUTEX(mtd_blkdevs_mutex);
static LIST_HEAD(blktrans_majors); static LIST_HEAD(blktrans_majors);
static DEFINE_MUTEX(blktrans_ref_mutex); static DEFINE_MUTEX(blktrans_ref_mutex);
@ -133,6 +132,10 @@ static int mtd_blktrans_thread(void *arg)
if (!req && !(req = blk_fetch_request(rq))) { if (!req && !(req = blk_fetch_request(rq))) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
set_current_state(TASK_RUNNING);
spin_unlock_irq(rq->queue_lock); spin_unlock_irq(rq->queue_lock);
schedule(); schedule();
spin_lock_irq(rq->queue_lock); spin_lock_irq(rq->queue_lock);
@ -176,54 +179,53 @@ static void mtd_blktrans_request(struct request_queue *rq)
static int blktrans_open(struct block_device *bdev, fmode_t mode) static int blktrans_open(struct block_device *bdev, fmode_t mode)
{ {
struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
int ret; int ret = 0;
if (!dev) if (!dev)
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
mutex_lock(&mtd_blkdevs_mutex);
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (!dev->mtd) { if (dev->open++)
ret = -ENXIO;
goto unlock; goto unlock;
kref_get(&dev->ref);
__module_get(dev->tr->owner);
if (dev->mtd) {
ret = dev->tr->open ? dev->tr->open(dev) : 0;
__get_mtd_device(dev->mtd);
} }
ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
/* Take another reference on the device so it won't go away till
last release */
if (!ret)
kref_get(&dev->ref);
unlock: unlock:
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
blktrans_dev_put(dev); blktrans_dev_put(dev);
mutex_unlock(&mtd_blkdevs_mutex);
return ret; return ret;
} }
static int blktrans_release(struct gendisk *disk, fmode_t mode) static int blktrans_release(struct gendisk *disk, fmode_t mode)
{ {
struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
int ret = -ENXIO; int ret = 0;
if (!dev) if (!dev)
return ret; return ret;
mutex_lock(&mtd_blkdevs_mutex);
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
/* Release one reference, we sure its not the last one here*/ if (--dev->open)
kref_put(&dev->ref, blktrans_dev_release);
if (!dev->mtd)
goto unlock; goto unlock;
ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; kref_put(&dev->ref, blktrans_dev_release);
module_put(dev->tr->owner);
if (dev->mtd) {
ret = dev->tr->release ? dev->tr->release(dev) : 0;
__put_mtd_device(dev->mtd);
}
unlock: unlock:
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
blktrans_dev_put(dev); blktrans_dev_put(dev);
mutex_unlock(&mtd_blkdevs_mutex);
return ret; return ret;
} }
@ -256,7 +258,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
if (!dev) if (!dev)
return ret; return ret;
mutex_lock(&mtd_blkdevs_mutex);
mutex_lock(&dev->lock); mutex_lock(&dev->lock);
if (!dev->mtd) if (!dev->mtd)
@ -271,7 +272,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
} }
unlock: unlock:
mutex_unlock(&dev->lock); mutex_unlock(&dev->lock);
mutex_unlock(&mtd_blkdevs_mutex);
blktrans_dev_put(dev); blktrans_dev_put(dev);
return ret; return ret;
} }
@ -385,9 +385,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
gd->queue = new->rq; gd->queue = new->rq;
__get_mtd_device(new->mtd);
__module_get(tr->owner);
/* Create processing thread */ /* Create processing thread */
/* TODO: workqueue ? */ /* TODO: workqueue ? */
new->thread = kthread_run(mtd_blktrans_thread, new, new->thread = kthread_run(mtd_blktrans_thread, new,
@ -410,8 +407,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
} }
return 0; return 0;
error4: error4:
module_put(tr->owner);
__put_mtd_device(new->mtd);
blk_cleanup_queue(new->rq); blk_cleanup_queue(new->rq);
error3: error3:
put_disk(new->disk); put_disk(new->disk);
@ -448,17 +443,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
blk_start_queue(old->rq); blk_start_queue(old->rq);
spin_unlock_irqrestore(&old->queue_lock, flags); spin_unlock_irqrestore(&old->queue_lock, flags);
/* Ask trans driver for release to the mtd device */ /* If the device is currently open, tell trans driver to close it,
then put mtd device, and don't touch it again */
mutex_lock(&old->lock); mutex_lock(&old->lock);
if (old->open && old->tr->release) { if (old->open) {
old->tr->release(old); if (old->tr->release)
old->open = 0; old->tr->release(old);
__put_mtd_device(old->mtd);
} }
__put_mtd_device(old->mtd);
module_put(old->tr->owner);
/* At that point, we don't touch the mtd anymore */
old->mtd = NULL; old->mtd = NULL;
mutex_unlock(&old->lock); mutex_unlock(&old->lock);
@ -508,13 +501,16 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
mutex_lock(&mtd_table_mutex); mutex_lock(&mtd_table_mutex);
ret = register_blkdev(tr->major, tr->name); ret = register_blkdev(tr->major, tr->name);
if (ret) { if (ret < 0) {
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
tr->name, tr->major, ret); tr->name, tr->major, ret);
mutex_unlock(&mtd_table_mutex); mutex_unlock(&mtd_table_mutex);
return ret; return ret;
} }
if (ret)
tr->major = ret;
tr->blkshift = ffs(tr->blksize) - 1; tr->blkshift = ffs(tr->blksize) - 1;
INIT_LIST_HEAD(&tr->devs); INIT_LIST_HEAD(&tr->devs);

View File

@ -30,8 +30,9 @@
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/blkpg.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
@ -478,6 +479,78 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
return ret; return ret;
} }
/*
* Copies (and truncates, if necessary) data from the larger struct,
* nand_ecclayout, to the smaller, deprecated layout struct,
* nand_ecclayout_user. This is necessary only to suppport the deprecated
* API ioctl ECCGETLAYOUT while allowing all new functionality to use
* nand_ecclayout flexibly (i.e. the struct may change size in new
* releases without requiring major rewrites).
*/
static int shrink_ecclayout(const struct nand_ecclayout *from,
struct nand_ecclayout_user *to)
{
int i;
if (!from || !to)
return -EINVAL;
memset(to, 0, sizeof(*to));
to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
for (i = 0; i < to->eccbytes; i++)
to->eccpos[i] = from->eccpos[i];
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
if (from->oobfree[i].length == 0 &&
from->oobfree[i].offset == 0)
break;
to->oobavail += from->oobfree[i].length;
to->oobfree[i] = from->oobfree[i];
}
return 0;
}
#ifdef CONFIG_MTD_PARTITIONS
static int mtd_blkpg_ioctl(struct mtd_info *mtd,
struct blkpg_ioctl_arg __user *arg)
{
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* Only master mtd device must be used to control partitions */
if (!mtd_is_master(mtd))
return -EINVAL;
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
return -EFAULT;
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
return -EFAULT;
switch (a.op) {
case BLKPG_ADD_PARTITION:
return mtd_add_partition(mtd, p.devname, p.start, p.length);
case BLKPG_DEL_PARTITION:
if (p.pno < 0)
return -EINVAL;
return mtd_del_partition(mtd, p.pno);
default:
return -EINVAL;
}
}
#endif
static int mtd_ioctl(struct file *file, u_int cmd, u_long arg) static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
{ {
struct mtd_file_info *mfi = file->private_data; struct mtd_file_info *mfi = file->private_data;
@ -514,6 +587,9 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
if (get_user(ur_idx, &(ur->regionindex))) if (get_user(ur_idx, &(ur->regionindex)))
return -EFAULT; return -EFAULT;
if (ur_idx >= mtd->numeraseregions)
return -EINVAL;
kr = &(mtd->eraseregions[ur_idx]); kr = &(mtd->eraseregions[ur_idx]);
if (put_user(kr->offset, &(ur->offset)) if (put_user(kr->offset, &(ur->offset))
@ -813,14 +889,23 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
} }
#endif #endif
/* This ioctl is being deprecated - it truncates the ecc layout */
case ECCGETLAYOUT: case ECCGETLAYOUT:
{ {
struct nand_ecclayout_user *usrlay;
if (!mtd->ecclayout) if (!mtd->ecclayout)
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (copy_to_user(argp, mtd->ecclayout, usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
sizeof(struct nand_ecclayout))) if (!usrlay)
return -EFAULT; return -ENOMEM;
shrink_ecclayout(mtd->ecclayout, usrlay);
if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
ret = -EFAULT;
kfree(usrlay);
break; break;
} }
@ -856,6 +941,22 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
break; break;
} }
#ifdef CONFIG_MTD_PARTITIONS
case BLKPG:
{
ret = mtd_blkpg_ioctl(mtd,
(struct blkpg_ioctl_arg __user *)arg);
break;
}
case BLKRRPART:
{
/* No reread partition feature. Just return ok */
ret = 0;
break;
}
#endif
default: default:
ret = -ENOTTY; ret = -ENOTTY;
} }
@ -1033,7 +1134,7 @@ static const struct file_operations mtd_fops = {
static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type, static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data) int flags, const char *dev_name, void *data)
{ {
return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC); return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
} }
static struct file_system_type mtd_inodefs_type = { static struct file_system_type mtd_inodefs_type = {

View File

@ -29,9 +29,11 @@
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/err.h>
/* Our partition linked list */ /* Our partition linked list */
static LIST_HEAD(mtd_partitions); static LIST_HEAD(mtd_partitions);
static DEFINE_MUTEX(mtd_partitions_mutex);
/* Our partition node structure */ /* Our partition node structure */
struct mtd_part { struct mtd_part {
@ -326,6 +328,12 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res; return res;
} }
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
kfree(p);
}
/* /*
* This function unregisters and destroy all slave MTD objects which are * This function unregisters and destroy all slave MTD objects which are
* attached to the given master MTD object. * attached to the given master MTD object.
@ -334,33 +342,42 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
int del_mtd_partitions(struct mtd_info *master) int del_mtd_partitions(struct mtd_info *master)
{ {
struct mtd_part *slave, *next; struct mtd_part *slave, *next;
int ret, err = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list) list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if (slave->master == master) { if (slave->master == master) {
ret = del_mtd_device(&slave->mtd);
if (ret < 0) {
err = ret;
continue;
}
list_del(&slave->list); list_del(&slave->list);
del_mtd_device(&slave->mtd); free_partition(slave);
kfree(slave);
} }
mutex_unlock(&mtd_partitions_mutex);
return 0; return err;
} }
EXPORT_SYMBOL(del_mtd_partitions); EXPORT_SYMBOL(del_mtd_partitions);
static struct mtd_part *add_one_partition(struct mtd_info *master, static struct mtd_part *allocate_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno, const struct mtd_partition *part, int partno,
uint64_t cur_offset) uint64_t cur_offset)
{ {
struct mtd_part *slave; struct mtd_part *slave;
char *name;
/* allocate the partition structure */ /* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL); slave = kzalloc(sizeof(*slave), GFP_KERNEL);
if (!slave) { name = kstrdup(part->name, GFP_KERNEL);
if (!name || !slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
master->name); master->name);
del_mtd_partitions(master); kfree(name);
return NULL; kfree(slave);
return ERR_PTR(-ENOMEM);
} }
list_add(&slave->list, &mtd_partitions);
/* set up the MTD object for this partition */ /* set up the MTD object for this partition */
slave->mtd.type = master->type; slave->mtd.type = master->type;
@ -371,7 +388,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.oobavail = master->oobavail; slave->mtd.oobavail = master->oobavail;
slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = part->name; slave->mtd.name = name;
slave->mtd.owner = master->owner; slave->mtd.owner = master->owner;
slave->mtd.backing_dev_info = master->backing_dev_info; slave->mtd.backing_dev_info = master->backing_dev_info;
@ -518,12 +535,89 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
} }
out_register: out_register:
/* register our partition */
add_mtd_device(&slave->mtd);
return slave; return slave;
} }
int mtd_add_partition(struct mtd_info *master, char *name,
long long offset, long long length)
{
struct mtd_partition part;
struct mtd_part *p, *new;
uint64_t start, end;
int ret = 0;
/* the direct offset is expected */
if (offset == MTDPART_OFS_APPEND ||
offset == MTDPART_OFS_NXTBLK)
return -EINVAL;
if (length == MTDPART_SIZ_FULL)
length = master->size - offset;
if (length <= 0)
return -EINVAL;
part.name = name;
part.size = length;
part.offset = offset;
part.mask_flags = 0;
part.ecclayout = NULL;
new = allocate_partition(master, &part, -1, offset);
if (IS_ERR(new))
return PTR_ERR(new);
start = offset;
end = offset + length;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry(p, &mtd_partitions, list)
if (p->master == master) {
if ((start >= p->offset) &&
(start < (p->offset + p->mtd.size)))
goto err_inv;
if ((end >= p->offset) &&
(end < (p->offset + p->mtd.size)))
goto err_inv;
}
list_add(&new->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
add_mtd_device(&new->mtd);
return ret;
err_inv:
mutex_unlock(&mtd_partitions_mutex);
free_partition(new);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mtd_add_partition);
int mtd_del_partition(struct mtd_info *master, int partno)
{
struct mtd_part *slave, *next;
int ret = -EINVAL;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if ((slave->master == master) &&
(slave->mtd.index == partno)) {
ret = del_mtd_device(&slave->mtd);
if (ret < 0)
break;
list_del(&slave->list);
free_partition(slave);
break;
}
mutex_unlock(&mtd_partitions_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(mtd_del_partition);
/* /*
* This function, given a master MTD object and a partition table, creates * This function, given a master MTD object and a partition table, creates
* and registers slave MTD objects which are bound to the master according to * and registers slave MTD objects which are bound to the master according to
@ -544,9 +638,16 @@ int add_mtd_partitions(struct mtd_info *master,
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) { for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset); slave = allocate_partition(master, parts + i, i, cur_offset);
if (!slave) if (IS_ERR(slave))
return -ENOMEM; return PTR_ERR(slave);
mutex_lock(&mtd_partitions_mutex);
list_add(&slave->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
add_mtd_device(&slave->mtd);
cur_offset = slave->offset + slave->mtd.size; cur_offset = slave->offset + slave->mtd.size;
} }
@ -618,3 +719,20 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(parse_mtd_partitions); EXPORT_SYMBOL_GPL(parse_mtd_partitions);
int mtd_is_master(struct mtd_info *mtd)
{
struct mtd_part *part;
int nopart = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry(part, &mtd_partitions, list)
if (&part->mtd == mtd) {
nopart = 1;
break;
}
mutex_unlock(&mtd_partitions_mutex);
return nopart;
}
EXPORT_SYMBOL_GPL(mtd_is_master);

View File

@ -400,13 +400,6 @@ config MTD_NAND_PXA3xx
This enables the driver for the NAND flash device found on This enables the driver for the NAND flash device found on
PXA3xx processors PXA3xx processors
config MTD_NAND_PXA3xx_BUILTIN
bool "Use builtin definitions for some NAND chips (deprecated)"
depends on MTD_NAND_PXA3xx
help
This enables builtin definitions for some NAND chips. This
is deprecated in favor of platform specific data.
config MTD_NAND_CM_X270 config MTD_NAND_CM_X270
tristate "Support for NAND Flash on CM-X270 modules" tristate "Support for NAND Flash on CM-X270 modules"
depends on MACH_ARMCORE depends on MACH_ARMCORE
@ -458,6 +451,7 @@ config MTD_NAND_ORION
config MTD_NAND_FSL_ELBC config MTD_NAND_FSL_ELBC
tristate "NAND support for Freescale eLBC controllers" tristate "NAND support for Freescale eLBC controllers"
depends on PPC_OF depends on PPC_OF
select FSL_LBC
help help
Various Freescale chips, including the 8313, include a NAND Flash Various Freescale chips, including the 8313, include a NAND Flash
Controller Module with built-in hardware ECC capabilities. Controller Module with built-in hardware ECC capabilities.
@ -531,4 +525,11 @@ config MTD_NAND_JZ4740
help help
Enables support for NAND Flash on JZ4740 SoC based boards. Enables support for NAND Flash on JZ4740 SoC based boards.
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
depends on PLAT_SPEAR || PLAT_NOMADIK || MACH_U300
help
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
endif # MTD_NAND endif # MTD_NAND

View File

@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o

View File

@ -110,15 +110,6 @@ static const unsigned short bfin_nfc_pin_req[] =
0}; 0};
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
static uint8_t bbt_pattern[] = { 0xff };
static struct nand_bbt_descr bootrom_bbt = {
.options = 0,
.offs = 63,
.len = 1,
.pattern = bbt_pattern,
};
static struct nand_ecclayout bootrom_ecclayout = { static struct nand_ecclayout bootrom_ecclayout = {
.eccbytes = 24, .eccbytes = 24,
.eccpos = { .eccpos = {
@ -809,7 +800,6 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
/* setup hardware ECC data struct */ /* setup hardware ECC data struct */
if (hardware_ecc) { if (hardware_ecc) {
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC #ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->badblock_pattern = &bootrom_bbt;
chip->ecc.layout = &bootrom_ecclayout; chip->ecc.layout = &bootrom_ecclayout;
#endif #endif
chip->read_buf = bf5xx_nand_dma_read_buf; chip->read_buf = bf5xx_nand_dma_read_buf;
@ -830,6 +820,10 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
goto out_err_nand_scan; goto out_err_nand_scan;
} }
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->badblockpos = 63;
#endif
/* add NAND partition */ /* add NAND partition */
bf5xx_nand_add_partition(info); bf5xx_nand_add_partition(info);

View File

@ -316,7 +316,7 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
u32 syndrome[4]; u32 syndrome[4];
u32 ecc_state; u32 ecc_state;
unsigned num_errors, corrected; unsigned num_errors, corrected;
unsigned long timeo = jiffies + msecs_to_jiffies(100); unsigned long timeo;
/* All bytes 0xff? It's an erased page; ignore its ECC. */ /* All bytes 0xff? It's an erased page; ignore its ECC. */
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
@ -372,9 +372,11 @@ compare:
* after setting the 4BITECC_ADD_CALC_START bit. So if you immediately * after setting the 4BITECC_ADD_CALC_START bit. So if you immediately
* begin trying to poll for the state, you may fall right out of your * begin trying to poll for the state, you may fall right out of your
* loop without any of the correction calculations having taken place. * loop without any of the correction calculations having taken place.
* The recommendation from the hardware team is to wait till ECC_STATE * The recommendation from the hardware team is to initially delay as
* reads less than 4, which means ECC HW has entered correction state. * long as ECC_STATE reads less than 4. After that, ECC HW has entered
* correction state.
*/ */
timeo = jiffies + usecs_to_jiffies(100);
do { do {
ecc_state = (davinci_nand_readl(info, ecc_state = (davinci_nand_readl(info,
NANDFSR_OFFSET) >> 8) & 0x0f; NANDFSR_OFFSET) >> 8) & 0x0f;
@ -733,6 +735,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
* breaks userspace ioctl interface with mtd-utils. Once we * breaks userspace ioctl interface with mtd-utils. Once we
* resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
* for the 4KiB page chips. * for the 4KiB page chips.
*
* TODO: Note that nand_ecclayout has now been expanded and can
* hold plenty of OOB entries.
*/ */
dev_warn(&pdev->dev, "no 4-bit ECC support yet " dev_warn(&pdev->dev, "no 4-bit ECC support yet "
"for 4KiB-page NAND\n"); "for 4KiB-page NAND\n");

View File

@ -1292,6 +1292,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
read_status(denali); read_status(denali);
break; break;
case NAND_CMD_READID: case NAND_CMD_READID:
case NAND_CMD_PARAM:
reset_buf(denali); reset_buf(denali);
/*sometimes ManufactureId read from register is not right /*sometimes ManufactureId read from register is not right
* e.g. some of Micron MT29F32G08QAA MLC NAND chips * e.g. some of Micron MT29F32G08QAA MLC NAND chips

View File

@ -1,9 +1,11 @@
/* Freescale Enhanced Local Bus Controller NAND driver /* Freescale Enhanced Local Bus Controller NAND driver
* *
* Copyright (c) 2006-2007 Freescale Semiconductor * Copyright © 2006-2007, 2010 Freescale Semiconductor
* *
* Authors: Nick Spence <nick.spence@freescale.com>, * Authors: Nick Spence <nick.spence@freescale.com>,
* Scott Wood <scottwood@freescale.com> * Scott Wood <scottwood@freescale.com>
* Jack Lan <jack.lan@freescale.com>
* Roy Zang <tie-fei.zang@freescale.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -27,6 +29,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
@ -42,14 +45,12 @@
#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */ #define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
struct fsl_elbc_ctrl;
/* mtd information per set */ /* mtd information per set */
struct fsl_elbc_mtd { struct fsl_elbc_mtd {
struct mtd_info mtd; struct mtd_info mtd;
struct nand_chip chip; struct nand_chip chip;
struct fsl_elbc_ctrl *ctrl; struct fsl_lbc_ctrl *ctrl;
struct device *dev; struct device *dev;
int bank; /* Chip select bank number */ int bank; /* Chip select bank number */
@ -58,18 +59,12 @@ struct fsl_elbc_mtd {
unsigned int fmr; /* FCM Flash Mode Register value */ unsigned int fmr; /* FCM Flash Mode Register value */
}; };
/* overview of the fsl elbc controller */ /* Freescale eLBC FCM controller infomation */
struct fsl_elbc_ctrl { struct fsl_elbc_fcm_ctrl {
struct nand_hw_control controller; struct nand_hw_control controller;
struct fsl_elbc_mtd *chips[MAX_BANKS]; struct fsl_elbc_mtd *chips[MAX_BANKS];
/* device info */
struct device *dev;
struct fsl_lbc_regs __iomem *regs;
int irq;
wait_queue_head_t irq_wait;
unsigned int irq_status; /* status read from LTESR by irq handler */
u8 __iomem *addr; /* Address of assigned FCM buffer */ u8 __iomem *addr; /* Address of assigned FCM buffer */
unsigned int page; /* Last page written to / read from */ unsigned int page; /* Last page written to / read from */
unsigned int read_bytes; /* Number of bytes read during command */ unsigned int read_bytes; /* Number of bytes read during command */
@ -79,6 +74,7 @@ struct fsl_elbc_ctrl {
unsigned int mdr; /* UPM/FCM Data Register value */ unsigned int mdr; /* UPM/FCM Data Register value */
unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int use_mdr; /* Non zero if the MDR is to be set */
unsigned int oob; /* Non zero if operating on OOB data */ unsigned int oob; /* Non zero if operating on OOB data */
unsigned int counter; /* counter for the initializations */
char *oob_poi; /* Place to write ECC after read back */ char *oob_poi; /* Place to write ECC after read back */
}; };
@ -164,11 +160,12 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
int buf_num; int buf_num;
ctrl->page = page_addr; elbc_fcm_ctrl->page = page_addr;
out_be32(&lbc->fbar, out_be32(&lbc->fbar,
page_addr >> (chip->phys_erase_shift - chip->page_shift)); page_addr >> (chip->phys_erase_shift - chip->page_shift));
@ -185,16 +182,18 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
buf_num = page_addr & 7; buf_num = page_addr & 7;
} }
ctrl->addr = priv->vbase + buf_num * 1024; elbc_fcm_ctrl->addr = priv->vbase + buf_num * 1024;
ctrl->index = column; elbc_fcm_ctrl->index = column;
/* for OOB data point to the second half of the buffer */ /* for OOB data point to the second half of the buffer */
if (oob) if (oob)
ctrl->index += priv->page_size ? 2048 : 512; elbc_fcm_ctrl->index += priv->page_size ? 2048 : 512;
dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), " dev_vdbg(priv->dev, "set_addr: bank=%d, "
"elbc_fcm_ctrl->addr=0x%p (0x%p), "
"index %x, pes %d ps %d\n", "index %x, pes %d ps %d\n",
buf_num, ctrl->addr, priv->vbase, ctrl->index, buf_num, elbc_fcm_ctrl->addr, priv->vbase,
elbc_fcm_ctrl->index,
chip->phys_erase_shift, chip->page_shift); chip->phys_erase_shift, chip->page_shift);
} }
@ -205,18 +204,19 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
/* Setup the FMR[OP] to execute without write protection */ /* Setup the FMR[OP] to execute without write protection */
out_be32(&lbc->fmr, priv->fmr | 3); out_be32(&lbc->fmr, priv->fmr | 3);
if (ctrl->use_mdr) if (elbc_fcm_ctrl->use_mdr)
out_be32(&lbc->mdr, ctrl->mdr); out_be32(&lbc->mdr, elbc_fcm_ctrl->mdr);
dev_vdbg(ctrl->dev, dev_vdbg(priv->dev,
"fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n", "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr)); in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
dev_vdbg(ctrl->dev, dev_vdbg(priv->dev,
"fsl_elbc_run_command: fbar=%08x fpar=%08x " "fsl_elbc_run_command: fbar=%08x fpar=%08x "
"fbcr=%08x bank=%d\n", "fbcr=%08x bank=%d\n",
in_be32(&lbc->fbar), in_be32(&lbc->fpar), in_be32(&lbc->fbar), in_be32(&lbc->fpar),
@ -229,19 +229,18 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
/* wait for FCM complete flag or timeout */ /* wait for FCM complete flag or timeout */
wait_event_timeout(ctrl->irq_wait, ctrl->irq_status, wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
FCM_TIMEOUT_MSECS * HZ/1000); FCM_TIMEOUT_MSECS * HZ/1000);
ctrl->status = ctrl->irq_status; elbc_fcm_ctrl->status = ctrl->irq_status;
/* store mdr value in case it was needed */ /* store mdr value in case it was needed */
if (ctrl->use_mdr) if (elbc_fcm_ctrl->use_mdr)
ctrl->mdr = in_be32(&lbc->mdr); elbc_fcm_ctrl->mdr = in_be32(&lbc->mdr);
ctrl->use_mdr = 0; elbc_fcm_ctrl->use_mdr = 0;
if (ctrl->status != LTESR_CC) { if (elbc_fcm_ctrl->status != LTESR_CC) {
dev_info(ctrl->dev, dev_info(priv->dev,
"command failed: fir %x fcr %x status %x mdr %x\n", "command failed: fir %x fcr %x status %x mdr %x\n",
in_be32(&lbc->fir), in_be32(&lbc->fcr), in_be32(&lbc->fir), in_be32(&lbc->fcr),
ctrl->status, ctrl->mdr); elbc_fcm_ctrl->status, elbc_fcm_ctrl->mdr);
return -EIO; return -EIO;
} }
@ -251,7 +250,7 @@ static int fsl_elbc_run_command(struct mtd_info *mtd)
static void fsl_elbc_do_read(struct nand_chip *chip, int oob) static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
{ {
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
if (priv->page_size) { if (priv->page_size) {
@ -284,15 +283,16 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
ctrl->use_mdr = 0; elbc_fcm_ctrl->use_mdr = 0;
/* clear the read buffer */ /* clear the read buffer */
ctrl->read_bytes = 0; elbc_fcm_ctrl->read_bytes = 0;
if (command != NAND_CMD_PAGEPROG) if (command != NAND_CMD_PAGEPROG)
ctrl->index = 0; elbc_fcm_ctrl->index = 0;
switch (command) { switch (command) {
/* READ0 and READ1 read the entire buffer to use hardware ECC. */ /* READ0 and READ1 read the entire buffer to use hardware ECC. */
@ -301,7 +301,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* fall-through */ /* fall-through */
case NAND_CMD_READ0: case NAND_CMD_READ0:
dev_dbg(ctrl->dev, dev_dbg(priv->dev,
"fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:" "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
" 0x%x, column: 0x%x.\n", page_addr, column); " 0x%x, column: 0x%x.\n", page_addr, column);
@ -309,8 +309,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */ out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
set_addr(mtd, 0, page_addr, 0); set_addr(mtd, 0, page_addr, 0);
ctrl->read_bytes = mtd->writesize + mtd->oobsize; elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
ctrl->index += column; elbc_fcm_ctrl->index += column;
fsl_elbc_do_read(chip, 0); fsl_elbc_do_read(chip, 0);
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
@ -318,14 +318,14 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* READOOB reads only the OOB because no ECC is performed. */ /* READOOB reads only the OOB because no ECC is performed. */
case NAND_CMD_READOOB: case NAND_CMD_READOOB:
dev_vdbg(ctrl->dev, dev_vdbg(priv->dev,
"fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:" "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
" 0x%x, column: 0x%x.\n", page_addr, column); " 0x%x, column: 0x%x.\n", page_addr, column);
out_be32(&lbc->fbcr, mtd->oobsize - column); out_be32(&lbc->fbcr, mtd->oobsize - column);
set_addr(mtd, column, page_addr, 1); set_addr(mtd, column, page_addr, 1);
ctrl->read_bytes = mtd->writesize + mtd->oobsize; elbc_fcm_ctrl->read_bytes = mtd->writesize + mtd->oobsize;
fsl_elbc_do_read(chip, 1); fsl_elbc_do_read(chip, 1);
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
@ -333,7 +333,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* READID must read all 5 possible bytes while CEB is active */ /* READID must read all 5 possible bytes while CEB is active */
case NAND_CMD_READID: case NAND_CMD_READID:
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) |
(FIR_OP_UA << FIR_OP1_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) |
@ -341,9 +341,9 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
/* 5 bytes for manuf, device and exts */ /* 5 bytes for manuf, device and exts */
out_be32(&lbc->fbcr, 5); out_be32(&lbc->fbcr, 5);
ctrl->read_bytes = 5; elbc_fcm_ctrl->read_bytes = 5;
ctrl->use_mdr = 1; elbc_fcm_ctrl->use_mdr = 1;
ctrl->mdr = 0; elbc_fcm_ctrl->mdr = 0;
set_addr(mtd, 0, 0, 0); set_addr(mtd, 0, 0, 0);
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
@ -351,7 +351,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* ERASE1 stores the block and page address */ /* ERASE1 stores the block and page address */
case NAND_CMD_ERASE1: case NAND_CMD_ERASE1:
dev_vdbg(ctrl->dev, dev_vdbg(priv->dev,
"fsl_elbc_cmdfunc: NAND_CMD_ERASE1, " "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
"page_addr: 0x%x.\n", page_addr); "page_addr: 0x%x.\n", page_addr);
set_addr(mtd, 0, page_addr, 0); set_addr(mtd, 0, page_addr, 0);
@ -359,7 +359,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* ERASE2 uses the block and page address from ERASE1 */ /* ERASE2 uses the block and page address from ERASE1 */
case NAND_CMD_ERASE2: case NAND_CMD_ERASE2:
dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n"); dev_vdbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
out_be32(&lbc->fir, out_be32(&lbc->fir,
(FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_CM0 << FIR_OP0_SHIFT) |
@ -374,8 +374,8 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
(NAND_CMD_ERASE2 << FCR_CMD2_SHIFT)); (NAND_CMD_ERASE2 << FCR_CMD2_SHIFT));
out_be32(&lbc->fbcr, 0); out_be32(&lbc->fbcr, 0);
ctrl->read_bytes = 0; elbc_fcm_ctrl->read_bytes = 0;
ctrl->use_mdr = 1; elbc_fcm_ctrl->use_mdr = 1;
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
return; return;
@ -383,14 +383,12 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* SEQIN sets up the addr buffer and all registers except the length */ /* SEQIN sets up the addr buffer and all registers except the length */
case NAND_CMD_SEQIN: { case NAND_CMD_SEQIN: {
__be32 fcr; __be32 fcr;
dev_vdbg(ctrl->dev, dev_vdbg(priv->dev,
"fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
"page_addr: 0x%x, column: 0x%x.\n", "page_addr: 0x%x, column: 0x%x.\n",
page_addr, column); page_addr, column);
ctrl->column = column; elbc_fcm_ctrl->use_mdr = 1;
ctrl->oob = 0;
ctrl->use_mdr = 1;
fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) | fcr = (NAND_CMD_STATUS << FCR_CMD1_SHIFT) |
(NAND_CMD_SEQIN << FCR_CMD2_SHIFT) | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT) |
@ -420,7 +418,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* OOB area --> READOOB */ /* 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; elbc_fcm_ctrl->oob = 1;
} else { } else {
WARN_ON(column != 0); WARN_ON(column != 0);
/* First 256 bytes --> READ0 */ /* First 256 bytes --> READ0 */
@ -429,24 +427,24 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
} }
out_be32(&lbc->fcr, fcr); out_be32(&lbc->fcr, fcr);
set_addr(mtd, column, page_addr, ctrl->oob); set_addr(mtd, column, page_addr, elbc_fcm_ctrl->oob);
return; return;
} }
/* PAGEPROG reuses all of the setup from SEQIN and adds the length */ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
case NAND_CMD_PAGEPROG: { case NAND_CMD_PAGEPROG: {
int full_page; int full_page;
dev_vdbg(ctrl->dev, dev_vdbg(priv->dev,
"fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
"writing %d bytes.\n", ctrl->index); "writing %d bytes.\n", elbc_fcm_ctrl->index);
/* if the write did not start at 0 or is not a full page /* if the write did not start at 0 or is not a full page
* then set the exact length, otherwise use a full page * then set the exact length, otherwise use a full page
* write so the HW generates the ECC. * write so the HW generates the ECC.
*/ */
if (ctrl->oob || ctrl->column != 0 || if (elbc_fcm_ctrl->oob || elbc_fcm_ctrl->column != 0 ||
ctrl->index != mtd->writesize + mtd->oobsize) { elbc_fcm_ctrl->index != mtd->writesize + mtd->oobsize) {
out_be32(&lbc->fbcr, ctrl->index); out_be32(&lbc->fbcr, elbc_fcm_ctrl->index);
full_page = 0; full_page = 0;
} else { } else {
out_be32(&lbc->fbcr, 0); out_be32(&lbc->fbcr, 0);
@ -458,21 +456,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
/* Read back the page in order to fill in the ECC for the /* Read back the page in order to fill in the ECC for the
* caller. Is this really needed? * caller. Is this really needed?
*/ */
if (full_page && ctrl->oob_poi) { if (full_page && elbc_fcm_ctrl->oob_poi) {
out_be32(&lbc->fbcr, 3); out_be32(&lbc->fbcr, 3);
set_addr(mtd, 6, page_addr, 1); set_addr(mtd, 6, page_addr, 1);
ctrl->read_bytes = mtd->writesize + 9; elbc_fcm_ctrl->read_bytes = mtd->writesize + 9;
fsl_elbc_do_read(chip, 1); fsl_elbc_do_read(chip, 1);
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
memcpy_fromio(ctrl->oob_poi + 6, memcpy_fromio(elbc_fcm_ctrl->oob_poi + 6,
&ctrl->addr[ctrl->index], 3); &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], 3);
ctrl->index += 3; elbc_fcm_ctrl->index += 3;
} }
ctrl->oob_poi = NULL; elbc_fcm_ctrl->oob_poi = NULL;
return; return;
} }
@ -485,26 +483,26 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
out_be32(&lbc->fbcr, 1); out_be32(&lbc->fbcr, 1);
set_addr(mtd, 0, 0, 0); set_addr(mtd, 0, 0, 0);
ctrl->read_bytes = 1; elbc_fcm_ctrl->read_bytes = 1;
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
/* 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); setbits8(elbc_fcm_ctrl->addr, NAND_STATUS_WP);
return; return;
/* RESET without waiting for the ready line */ /* RESET without waiting for the ready line */
case NAND_CMD_RESET: case NAND_CMD_RESET:
dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); dev_dbg(priv->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
fsl_elbc_run_command(mtd); fsl_elbc_run_command(mtd);
return; return;
default: default:
dev_err(ctrl->dev, dev_err(priv->dev,
"fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
command); command);
} }
@ -524,24 +522,24 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
unsigned int bufsize = mtd->writesize + mtd->oobsize; unsigned int bufsize = mtd->writesize + mtd->oobsize;
if (len <= 0) { if (len <= 0) {
dev_err(ctrl->dev, "write_buf of %d bytes", len); dev_err(priv->dev, "write_buf of %d bytes", len);
ctrl->status = 0; elbc_fcm_ctrl->status = 0;
return; return;
} }
if ((unsigned int)len > bufsize - ctrl->index) { if ((unsigned int)len > bufsize - elbc_fcm_ctrl->index) {
dev_err(ctrl->dev, dev_err(priv->dev,
"write_buf beyond end of buffer " "write_buf beyond end of buffer "
"(%d requested, %u available)\n", "(%d requested, %u available)\n",
len, bufsize - ctrl->index); len, bufsize - elbc_fcm_ctrl->index);
len = bufsize - ctrl->index; len = bufsize - elbc_fcm_ctrl->index;
} }
memcpy_toio(&ctrl->addr[ctrl->index], buf, len); memcpy_toio(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], buf, len);
/* /*
* This is workaround for the weird elbc hangs during nand write, * This is workaround for the weird elbc hangs during nand write,
* Scott Wood says: "...perhaps difference in how long it takes a * Scott Wood says: "...perhaps difference in how long it takes a
@ -549,9 +547,9 @@ static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
* is causing problems, and sync isn't helping for some reason." * is causing problems, and sync isn't helping for some reason."
* Reading back the last byte helps though. * Reading back the last byte helps though.
*/ */
in_8(&ctrl->addr[ctrl->index] + len - 1); in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index] + len - 1);
ctrl->index += len; elbc_fcm_ctrl->index += len;
} }
/* /*
@ -562,13 +560,13 @@ static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
/* If there are still bytes in the FCM, then use the next byte. */ /* If there are still bytes in the FCM, then use the next byte. */
if (ctrl->index < ctrl->read_bytes) if (elbc_fcm_ctrl->index < elbc_fcm_ctrl->read_bytes)
return in_8(&ctrl->addr[ctrl->index++]); return in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index++]);
dev_err(ctrl->dev, "read_byte beyond end of buffer\n"); dev_err(priv->dev, "read_byte beyond end of buffer\n");
return ERR_BYTE; return ERR_BYTE;
} }
@ -579,18 +577,19 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
int avail; int avail;
if (len < 0) if (len < 0)
return; return;
avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); avail = min((unsigned int)len,
memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
ctrl->index += avail; memcpy_fromio(buf, &elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index], avail);
elbc_fcm_ctrl->index += avail;
if (len > avail) if (len > avail)
dev_err(ctrl->dev, dev_err(priv->dev,
"read_buf beyond end of buffer " "read_buf beyond end of buffer "
"(%d requested, %d available)\n", "(%d requested, %d available)\n",
len, avail); len, avail);
@ -603,30 +602,32 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
int i; int i;
if (len < 0) { if (len < 0) {
dev_err(ctrl->dev, "write_buf of %d bytes", len); dev_err(priv->dev, "write_buf of %d bytes", len);
return -EINVAL; return -EINVAL;
} }
if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { if ((unsigned int)len >
dev_err(ctrl->dev, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index) {
"verify_buf beyond end of buffer " dev_err(priv->dev,
"(%d requested, %u available)\n", "verify_buf beyond end of buffer "
len, ctrl->read_bytes - ctrl->index); "(%d requested, %u available)\n",
len, elbc_fcm_ctrl->read_bytes - elbc_fcm_ctrl->index);
ctrl->index = ctrl->read_bytes; elbc_fcm_ctrl->index = elbc_fcm_ctrl->read_bytes;
return -EINVAL; return -EINVAL;
} }
for (i = 0; i < len; i++) for (i = 0; i < len; i++)
if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) if (in_8(&elbc_fcm_ctrl->addr[elbc_fcm_ctrl->index + i])
!= buf[i])
break; break;
ctrl->index += len; elbc_fcm_ctrl->index += len;
return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; return i == len && elbc_fcm_ctrl->status == LTESR_CC ? 0 : -EIO;
} }
/* This function is called after Program and Erase Operations to /* This function is called after Program and Erase Operations to
@ -635,22 +636,22 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) 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_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
if (ctrl->status != LTESR_CC) if (elbc_fcm_ctrl->status != LTESR_CC)
return NAND_STATUS_FAIL; return NAND_STATUS_FAIL;
/* 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.
*/ */
return (ctrl->mdr & 0xff) | NAND_STATUS_WP; return (elbc_fcm_ctrl->mdr & 0xff) | NAND_STATUS_WP;
} }
static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
{ {
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
unsigned int al; unsigned int al;
@ -665,41 +666,41 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */ priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */
(al << FMR_AL_SHIFT); (al << FMR_AL_SHIFT);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->numchips = %d\n",
chip->numchips); chip->numchips);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %lld\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->chipsize = %lld\n",
chip->chipsize); chip->chipsize);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
chip->pagemask); chip->pagemask);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
chip->chip_delay); chip->chip_delay);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
chip->badblockpos); chip->badblockpos);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
chip->chip_shift); chip->chip_shift);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->page_shift = %d\n",
chip->page_shift); chip->page_shift);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
chip->phys_erase_shift); chip->phys_erase_shift);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
chip->ecclayout); chip->ecclayout);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
chip->ecc.mode); chip->ecc.mode);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
chip->ecc.steps); chip->ecc.steps);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
chip->ecc.bytes); chip->ecc.bytes);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
chip->ecc.total); chip->ecc.total);
dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
chip->ecc.layout); chip->ecc.layout);
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); dev_dbg(priv->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size); dev_dbg(priv->dev, "fsl_elbc_init: mtd->size = %lld\n", mtd->size);
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
mtd->erasesize); mtd->erasesize);
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: mtd->writesize = %d\n",
mtd->writesize); mtd->writesize);
dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n", dev_dbg(priv->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
mtd->oobsize); mtd->oobsize);
/* adjust Option Register and ECC to match Flash page size */ /* adjust Option Register and ECC to match Flash page size */
@ -719,7 +720,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
chip->badblock_pattern = &largepage_memorybased; chip->badblock_pattern = &largepage_memorybased;
} }
} else { } else {
dev_err(ctrl->dev, dev_err(priv->dev,
"fsl_elbc_init: page size %d is not supported\n", "fsl_elbc_init: page size %d is not supported\n",
mtd->writesize); mtd->writesize);
return -1; return -1;
@ -750,18 +751,19 @@ static void fsl_elbc_write_page(struct mtd_info *mtd,
const uint8_t *buf) const uint8_t *buf)
{ {
struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_mtd *priv = chip->priv;
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, buf, mtd->writesize);
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
ctrl->oob_poi = chip->oob_poi; elbc_fcm_ctrl->oob_poi = chip->oob_poi;
} }
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
{ {
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_ctrl *ctrl = priv->ctrl;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand;
struct nand_chip *chip = &priv->chip; struct nand_chip *chip = &priv->chip;
dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank); dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
@ -790,7 +792,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR |
NAND_USE_FLASH_BBT; NAND_USE_FLASH_BBT;
chip->controller = &ctrl->controller; chip->controller = &elbc_fcm_ctrl->controller;
chip->priv = priv; chip->priv = priv;
chip->ecc.read_page = fsl_elbc_read_page; chip->ecc.read_page = fsl_elbc_read_page;
@ -815,8 +817,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
{ {
struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = priv->ctrl->nand;
nand_release(&priv->mtd); nand_release(&priv->mtd);
kfree(priv->mtd.name); kfree(priv->mtd.name);
@ -824,18 +825,21 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
if (priv->vbase) if (priv->vbase)
iounmap(priv->vbase); iounmap(priv->vbase);
ctrl->chips[priv->bank] = NULL; elbc_fcm_ctrl->chips[priv->bank] = NULL;
kfree(priv); kfree(priv);
kfree(elbc_fcm_ctrl);
return 0; return 0;
} }
static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, static DEFINE_MUTEX(fsl_elbc_nand_mutex);
struct device_node *node)
static int __devinit fsl_elbc_nand_probe(struct platform_device *pdev)
{ {
struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_lbc_regs __iomem *lbc;
struct fsl_elbc_mtd *priv; struct fsl_elbc_mtd *priv;
struct resource res; struct resource res;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl;
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
static const char *part_probe_types[] static const char *part_probe_types[]
= { "cmdlinepart", "RedBoot", NULL }; = { "cmdlinepart", "RedBoot", NULL };
@ -843,11 +847,18 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
#endif #endif
int ret; int ret;
int bank; int bank;
struct device *dev;
struct device_node *node = pdev->dev.of_node;
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
lbc = fsl_lbc_ctrl_dev->regs;
dev = fsl_lbc_ctrl_dev->dev;
/* get, allocate and map the memory resource */ /* get, allocate and map the memory resource */
ret = of_address_to_resource(node, 0, &res); ret = of_address_to_resource(node, 0, &res);
if (ret) { if (ret) {
dev_err(ctrl->dev, "failed to get resource\n"); dev_err(dev, "failed to get resource\n");
return ret; return ret;
} }
@ -857,11 +868,11 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
(in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM && (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
(in_be32(&lbc->bank[bank].br) & (in_be32(&lbc->bank[bank].br) &
in_be32(&lbc->bank[bank].or) & BR_BA) in_be32(&lbc->bank[bank].or) & BR_BA)
== res.start) == fsl_lbc_addr(res.start))
break; break;
if (bank >= MAX_BANKS) { if (bank >= MAX_BANKS) {
dev_err(ctrl->dev, "address did not match any chip selects\n"); dev_err(dev, "address did not match any chip selects\n");
return -ENODEV; return -ENODEV;
} }
@ -869,14 +880,33 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
ctrl->chips[bank] = priv; mutex_lock(&fsl_elbc_nand_mutex);
if (!fsl_lbc_ctrl_dev->nand) {
elbc_fcm_ctrl = kzalloc(sizeof(*elbc_fcm_ctrl), GFP_KERNEL);
if (!elbc_fcm_ctrl) {
dev_err(dev, "failed to allocate memory\n");
mutex_unlock(&fsl_elbc_nand_mutex);
ret = -ENOMEM;
goto err;
}
elbc_fcm_ctrl->counter++;
spin_lock_init(&elbc_fcm_ctrl->controller.lock);
init_waitqueue_head(&elbc_fcm_ctrl->controller.wq);
fsl_lbc_ctrl_dev->nand = elbc_fcm_ctrl;
} else {
elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
}
mutex_unlock(&fsl_elbc_nand_mutex);
elbc_fcm_ctrl->chips[bank] = priv;
priv->bank = bank; priv->bank = bank;
priv->ctrl = ctrl; priv->ctrl = fsl_lbc_ctrl_dev;
priv->dev = ctrl->dev; priv->dev = dev;
priv->vbase = ioremap(res.start, resource_size(&res)); priv->vbase = ioremap(res.start, resource_size(&res));
if (!priv->vbase) { if (!priv->vbase) {
dev_err(ctrl->dev, "failed to map chip region\n"); dev_err(dev, "failed to map chip region\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err; goto err;
} }
@ -933,171 +963,53 @@ err:
return ret; return ret;
} }
static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) static int fsl_elbc_nand_remove(struct platform_device *pdev)
{ {
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
/*
* NAND transactions can tie up the bus for a long time, so set the
* bus timeout to max by clearing LBCR[BMT] (highest base counter
* value) and setting LBCR[BMTPS] to the highest prescaler value.
*/
clrsetbits_be32(&lbc->lbcr, LBCR_BMT, 15);
/* clear event registers */
setbits32(&lbc->ltesr, LTESR_NAND_MASK);
out_be32(&lbc->lteatr, 0);
/* Enable interrupts for any detected events */
out_be32(&lbc->lteir, LTESR_NAND_MASK);
ctrl->read_bytes = 0;
ctrl->index = 0;
ctrl->addr = NULL;
return 0;
}
static int fsl_elbc_ctrl_remove(struct platform_device *ofdev)
{
struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
int i; int i;
struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = fsl_lbc_ctrl_dev->nand;
for (i = 0; i < MAX_BANKS; i++) for (i = 0; i < MAX_BANKS; i++)
if (ctrl->chips[i]) if (elbc_fcm_ctrl->chips[i])
fsl_elbc_chip_remove(ctrl->chips[i]); fsl_elbc_chip_remove(elbc_fcm_ctrl->chips[i]);
if (ctrl->irq) mutex_lock(&fsl_elbc_nand_mutex);
free_irq(ctrl->irq, ctrl); elbc_fcm_ctrl->counter--;
if (!elbc_fcm_ctrl->counter) {
if (ctrl->regs) fsl_lbc_ctrl_dev->nand = NULL;
iounmap(ctrl->regs); kfree(elbc_fcm_ctrl);
dev_set_drvdata(&ofdev->dev, NULL);
kfree(ctrl);
return 0;
}
/* NOTE: This interrupt is also used to report other localbus events,
* such as transaction errors on other chipselects. If we want to
* capture those, we'll need to move the IRQ code into a shared
* LBC driver.
*/
static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
{
struct fsl_elbc_ctrl *ctrl = data;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
__be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK;
if (status) {
out_be32(&lbc->ltesr, status);
out_be32(&lbc->lteatr, 0);
ctrl->irq_status = status;
smp_wmb();
wake_up(&ctrl->irq_wait);
return IRQ_HANDLED;
} }
mutex_unlock(&fsl_elbc_nand_mutex);
return IRQ_NONE;
}
/* fsl_elbc_ctrl_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code allocates all of
* the resources needed for the controller only. The
* resources for the NAND banks themselves are allocated
* in the chip probe function.
*/
static int __devinit fsl_elbc_ctrl_probe(struct platform_device *ofdev,
const struct of_device_id *match)
{
struct device_node *child;
struct fsl_elbc_ctrl *ctrl;
int ret;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl)
return -ENOMEM;
dev_set_drvdata(&ofdev->dev, ctrl);
spin_lock_init(&ctrl->controller.lock);
init_waitqueue_head(&ctrl->controller.wq);
init_waitqueue_head(&ctrl->irq_wait);
ctrl->regs = of_iomap(ofdev->dev.of_node, 0);
if (!ctrl->regs) {
dev_err(&ofdev->dev, "failed to get memory region\n");
ret = -ENODEV;
goto err;
}
ctrl->irq = of_irq_to_resource(ofdev->dev.of_node, 0, NULL);
if (ctrl->irq == NO_IRQ) {
dev_err(&ofdev->dev, "failed to get irq resource\n");
ret = -ENODEV;
goto err;
}
ctrl->dev = &ofdev->dev;
ret = fsl_elbc_ctrl_init(ctrl);
if (ret < 0)
goto err;
ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl);
if (ret != 0) {
dev_err(&ofdev->dev, "failed to install irq (%d)\n",
ctrl->irq);
ret = ctrl->irq;
goto err;
}
for_each_child_of_node(ofdev->dev.of_node, child)
if (of_device_is_compatible(child, "fsl,elbc-fcm-nand"))
fsl_elbc_chip_probe(ctrl, child);
return 0; return 0;
err:
fsl_elbc_ctrl_remove(ofdev);
return ret;
} }
static const struct of_device_id fsl_elbc_match[] = { static const struct of_device_id fsl_elbc_nand_match[] = {
{ { .compatible = "fsl,elbc-fcm-nand", },
.compatible = "fsl,elbc",
},
{} {}
}; };
static struct of_platform_driver fsl_elbc_ctrl_driver = { static struct platform_driver fsl_elbc_nand_driver = {
.driver = { .driver = {
.name = "fsl-elbc", .name = "fsl,elbc-fcm-nand",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = fsl_elbc_match, .of_match_table = fsl_elbc_nand_match,
}, },
.probe = fsl_elbc_ctrl_probe, .probe = fsl_elbc_nand_probe,
.remove = fsl_elbc_ctrl_remove, .remove = fsl_elbc_nand_remove,
}; };
static int __init fsl_elbc_init(void) static int __init fsl_elbc_nand_init(void)
{ {
return of_register_platform_driver(&fsl_elbc_ctrl_driver); return platform_driver_register(&fsl_elbc_nand_driver);
} }
static void __exit fsl_elbc_exit(void) static void __exit fsl_elbc_nand_exit(void)
{ {
of_unregister_platform_driver(&fsl_elbc_ctrl_driver); platform_driver_unregister(&fsl_elbc_nand_driver);
} }
module_init(fsl_elbc_init); module_init(fsl_elbc_nand_init);
module_exit(fsl_elbc_exit); module_exit(fsl_elbc_nand_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Freescale"); MODULE_AUTHOR("Freescale");

View File

@ -186,7 +186,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
if (!flash_np) if (!flash_np)
return -ENODEV; return -ENODEV;
fun->mtd.name = kasprintf(GFP_KERNEL, "%x.%s", io_res->start, fun->mtd.name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start,
flash_np->name); flash_np->name);
if (!fun->mtd.name) { if (!fun->mtd.name) {
ret = -ENOMEM; ret = -ENOMEM;
@ -222,7 +222,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
{ {
struct fsl_upm_nand *fun; struct fsl_upm_nand *fun;
struct resource io_res; struct resource io_res;
const uint32_t *prop; const __be32 *prop;
int rnb_gpio; int rnb_gpio;
int ret; int ret;
int size; int size;
@ -270,7 +270,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
goto err1; goto err1;
} }
for (i = 0; i < fun->mchip_count; i++) for (i = 0; i < fun->mchip_count; i++)
fun->mchip_offsets[i] = prop[i]; fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
} else { } else {
fun->mchip_count = 1; fun->mchip_count = 1;
} }
@ -295,13 +295,13 @@ static int __devinit fun_probe(struct platform_device *ofdev,
prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL); prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL);
if (prop) if (prop)
fun->chip_delay = *prop; fun->chip_delay = be32_to_cpup(prop);
else else
fun->chip_delay = 50; fun->chip_delay = 50;
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size); prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
if (prop && size == sizeof(uint32_t)) if (prop && size == sizeof(uint32_t))
fun->wait_flags = *prop; fun->wait_flags = be32_to_cpup(prop);
else else
fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN | fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
FSL_UPM_WAIT_WRITE_BYTE; FSL_UPM_WAIT_WRITE_BYTE;

View File

@ -0,0 +1,866 @@
/*
* drivers/mtd/nand/fsmc_nand.c
*
* ST Microelectronics
* Flexible Static Memory Controller (FSMC)
* Driver for NAND portions
*
* Copyright © 2010 ST Microelectronics
* Vipin Kumar <vipin.kumar@st.com>
* Ashish Priyadarshi
*
* Based on drivers/mtd/nand/nomadik_nand.c
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/resource.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/platform_device.h>
#include <linux/mtd/partitions.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/mtd/fsmc.h>
#include <mtd/mtd-abi.h>
static struct nand_ecclayout fsmc_ecc1_layout = {
.eccbytes = 24,
.eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52,
66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116},
.oobfree = {
{.offset = 8, .length = 8},
{.offset = 24, .length = 8},
{.offset = 40, .length = 8},
{.offset = 56, .length = 8},
{.offset = 72, .length = 8},
{.offset = 88, .length = 8},
{.offset = 104, .length = 8},
{.offset = 120, .length = 8}
}
};
static struct nand_ecclayout fsmc_ecc4_lp_layout = {
.eccbytes = 104,
.eccpos = { 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14,
18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30,
34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46,
50, 51, 52, 53, 54, 55, 56,
57, 58, 59, 60, 61, 62,
66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78,
82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94,
98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110,
114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126
},
.oobfree = {
{.offset = 15, .length = 3},
{.offset = 31, .length = 3},
{.offset = 47, .length = 3},
{.offset = 63, .length = 3},
{.offset = 79, .length = 3},
{.offset = 95, .length = 3},
{.offset = 111, .length = 3},
{.offset = 127, .length = 1}
}
};
/*
* ECC placement definitions in oobfree type format.
* There are 13 bytes of ecc for every 512 byte block and it has to be read
* consecutively and immediately after the 512 byte data block for hardware to
* generate the error bit offsets in 512 byte data.
* Managing the ecc bytes in the following way makes it easier for software to
* read ecc bytes consecutive to data bytes. This way is similar to
* oobfree structure maintained already in generic nand driver
*/
static struct fsmc_eccplace fsmc_ecc4_lp_place = {
.eccplace = {
{.offset = 2, .length = 13},
{.offset = 18, .length = 13},
{.offset = 34, .length = 13},
{.offset = 50, .length = 13},
{.offset = 66, .length = 13},
{.offset = 82, .length = 13},
{.offset = 98, .length = 13},
{.offset = 114, .length = 13}
}
};
static struct nand_ecclayout fsmc_ecc4_sp_layout = {
.eccbytes = 13,
.eccpos = { 0, 1, 2, 3, 6, 7, 8,
9, 10, 11, 12, 13, 14
},
.oobfree = {
{.offset = 15, .length = 1},
}
};
static struct fsmc_eccplace fsmc_ecc4_sp_place = {
.eccplace = {
{.offset = 0, .length = 4},
{.offset = 6, .length = 9}
}
};
/*
* Default partition tables to be used if the partition information not
* provided through platform data
*/
#define PARTITION(n, off, sz) {.name = n, .offset = off, .size = sz}
/*
* Default partition layout for small page(= 512 bytes) devices
* Size for "Root file system" is updated in driver based on actual device size
*/
static struct mtd_partition partition_info_16KB_blk[] = {
PARTITION("X-loader", 0, 4 * 0x4000),
PARTITION("U-Boot", 0x10000, 20 * 0x4000),
PARTITION("Kernel", 0x60000, 256 * 0x4000),
PARTITION("Root File System", 0x460000, 0),
};
/*
* Default partition layout for large page(> 512 bytes) devices
* Size for "Root file system" is updated in driver based on actual device size
*/
static struct mtd_partition partition_info_128KB_blk[] = {
PARTITION("X-loader", 0, 4 * 0x20000),
PARTITION("U-Boot", 0x80000, 12 * 0x20000),
PARTITION("Kernel", 0x200000, 48 * 0x20000),
PARTITION("Root File System", 0x800000, 0),
};
#ifdef CONFIG_MTD_CMDLINE_PARTS
const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/**
* struct fsmc_nand_data - atructure for FSMC NAND device state
*
* @mtd: MTD info for a NAND flash.
* @nand: Chip related info for a NAND flash.
* @partitions: Partition info for a NAND Flash.
* @nr_partitions: Total number of partition of a NAND flash.
*
* @ecc_place: ECC placing locations in oobfree type format.
* @bank: Bank number for probed device.
* @clk: Clock structure for FSMC.
*
* @data_va: NAND port for Data.
* @cmd_va: NAND port for Command.
* @addr_va: NAND port for Address.
* @regs_va: FSMC regs base address.
*/
struct fsmc_nand_data {
struct mtd_info mtd;
struct nand_chip nand;
struct mtd_partition *partitions;
unsigned int nr_partitions;
struct fsmc_eccplace *ecc_place;
unsigned int bank;
struct clk *clk;
struct resource *resregs;
struct resource *rescmd;
struct resource *resaddr;
struct resource *resdata;
void __iomem *data_va;
void __iomem *cmd_va;
void __iomem *addr_va;
void __iomem *regs_va;
void (*select_chip)(uint32_t bank, uint32_t busw);
};
/* Assert CS signal based on chipnr */
static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
{
struct nand_chip *chip = mtd->priv;
struct fsmc_nand_data *host;
host = container_of(mtd, struct fsmc_nand_data, mtd);
switch (chipnr) {
case -1:
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
break;
case 0:
case 1:
case 2:
case 3:
if (host->select_chip)
host->select_chip(chipnr,
chip->options & NAND_BUSWIDTH_16);
break;
default:
BUG();
}
}
/*
* fsmc_cmd_ctrl - For facilitaing Hardware access
* This routine allows hardware specific access to control-lines(ALE,CLE)
*/
static void fsmc_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *this = mtd->priv;
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
struct fsmc_regs *regs = host->regs_va;
unsigned int bank = host->bank;
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_CLE) {
this->IO_ADDR_R = (void __iomem *)host->cmd_va;
this->IO_ADDR_W = (void __iomem *)host->cmd_va;
} else if (ctrl & NAND_ALE) {
this->IO_ADDR_R = (void __iomem *)host->addr_va;
this->IO_ADDR_W = (void __iomem *)host->addr_va;
} else {
this->IO_ADDR_R = (void __iomem *)host->data_va;
this->IO_ADDR_W = (void __iomem *)host->data_va;
}
if (ctrl & NAND_NCE) {
writel(readl(&regs->bank_regs[bank].pc) | FSMC_ENABLE,
&regs->bank_regs[bank].pc);
} else {
writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ENABLE,
&regs->bank_regs[bank].pc);
}
}
mb();
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
}
/*
* fsmc_nand_setup - FSMC (Flexible Static Memory Controller) init routine
*
* This routine initializes timing parameters related to NAND memory access in
* FSMC registers
*/
static void __init fsmc_nand_setup(struct fsmc_regs *regs, uint32_t bank,
uint32_t busw)
{
uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
if (busw)
writel(value | FSMC_DEVWID_16, &regs->bank_regs[bank].pc);
else
writel(value | FSMC_DEVWID_8, &regs->bank_regs[bank].pc);
writel(readl(&regs->bank_regs[bank].pc) | FSMC_TCLR_1 | FSMC_TAR_1,
&regs->bank_regs[bank].pc);
writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
&regs->bank_regs[bank].comm);
writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0,
&regs->bank_regs[bank].attrib);
}
/*
* fsmc_enable_hwecc - Enables Hardware ECC through FSMC registers
*/
static void fsmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
struct fsmc_regs *regs = host->regs_va;
uint32_t bank = host->bank;
writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCPLEN_256,
&regs->bank_regs[bank].pc);
writel(readl(&regs->bank_regs[bank].pc) & ~FSMC_ECCEN,
&regs->bank_regs[bank].pc);
writel(readl(&regs->bank_regs[bank].pc) | FSMC_ECCEN,
&regs->bank_regs[bank].pc);
}
/*
* fsmc_read_hwecc_ecc4 - Hardware ECC calculator for ecc4 option supported by
* FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction upto
* max of 8-bits)
*/
static int fsmc_read_hwecc_ecc4(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
struct fsmc_regs *regs = host->regs_va;
uint32_t bank = host->bank;
uint32_t ecc_tmp;
unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
do {
if (readl(&regs->bank_regs[bank].sts) & FSMC_CODE_RDY)
break;
else
cond_resched();
} while (!time_after_eq(jiffies, deadline));
ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
ecc[0] = (uint8_t) (ecc_tmp >> 0);
ecc[1] = (uint8_t) (ecc_tmp >> 8);
ecc[2] = (uint8_t) (ecc_tmp >> 16);
ecc[3] = (uint8_t) (ecc_tmp >> 24);
ecc_tmp = readl(&regs->bank_regs[bank].ecc2);
ecc[4] = (uint8_t) (ecc_tmp >> 0);
ecc[5] = (uint8_t) (ecc_tmp >> 8);
ecc[6] = (uint8_t) (ecc_tmp >> 16);
ecc[7] = (uint8_t) (ecc_tmp >> 24);
ecc_tmp = readl(&regs->bank_regs[bank].ecc3);
ecc[8] = (uint8_t) (ecc_tmp >> 0);
ecc[9] = (uint8_t) (ecc_tmp >> 8);
ecc[10] = (uint8_t) (ecc_tmp >> 16);
ecc[11] = (uint8_t) (ecc_tmp >> 24);
ecc_tmp = readl(&regs->bank_regs[bank].sts);
ecc[12] = (uint8_t) (ecc_tmp >> 16);
return 0;
}
/*
* fsmc_read_hwecc_ecc1 - Hardware ECC calculator for ecc1 option supported by
* FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction upto
* max of 1-bit)
*/
static int fsmc_read_hwecc_ecc1(struct mtd_info *mtd, const uint8_t *data,
uint8_t *ecc)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
struct fsmc_regs *regs = host->regs_va;
uint32_t bank = host->bank;
uint32_t ecc_tmp;
ecc_tmp = readl(&regs->bank_regs[bank].ecc1);
ecc[0] = (uint8_t) (ecc_tmp >> 0);
ecc[1] = (uint8_t) (ecc_tmp >> 8);
ecc[2] = (uint8_t) (ecc_tmp >> 16);
return 0;
}
/*
* fsmc_read_page_hwecc
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
* @page: page number to read
*
* This routine is needed for fsmc verison 8 as reading from NAND chip has to be
* performed in a strict sequence as follows:
* data(512 byte) -> ecc(13 byte)
* After this read, fsmc hardware generates and reports error data bits(upto a
* max of 8 bits)
*/
static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
struct fsmc_eccplace *ecc_place = host->ecc_place;
int i, j, s, stat, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *ecc_code = chip->buffers->ecccode;
int off, len, group = 0;
/*
* ecc_oob is intentionally taken as uint16_t. In 16bit devices, we
* end up reading 14 bytes (7 words) from oob. The local array is
* to maintain word alignment
*/
uint16_t ecc_oob[7];
uint8_t *oob = (uint8_t *)&ecc_oob[0];
for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page);
chip->ecc.hwctl(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
for (j = 0; j < eccbytes;) {
off = ecc_place->eccplace[group].offset;
len = ecc_place->eccplace[group].length;
group++;
/*
* length is intentionally kept a higher multiple of 2
* to read at least 13 bytes even in case of 16 bit NAND
* devices
*/
len = roundup(len, 2);
chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page);
chip->read_buf(mtd, oob + j, len);
j += len;
}
memcpy(&ecc_code[i], oob, 13);
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
if (stat < 0)
mtd->ecc_stats.failed++;
else
mtd->ecc_stats.corrected += stat;
}
return 0;
}
/*
* fsmc_correct_data
* @mtd: mtd info structure
* @dat: buffer of read data
* @read_ecc: ecc read from device spare area
* @calc_ecc: ecc calculated from read data
*
* calc_ecc is a 104 bit information containing maximum of 8 error
* offset informations of 13 bits each in 512 bytes of read data.
*/
static int fsmc_correct_data(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc, uint8_t *calc_ecc)
{
struct fsmc_nand_data *host = container_of(mtd,
struct fsmc_nand_data, mtd);
struct fsmc_regs *regs = host->regs_va;
unsigned int bank = host->bank;
uint16_t err_idx[8];
uint64_t ecc_data[2];
uint32_t num_err, i;
/* The calculated ecc is actually the correction index in data */
memcpy(ecc_data, calc_ecc, 13);
/*
* ------------------- calc_ecc[] bit wise -----------|--13 bits--|
* |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
*
* calc_ecc is a 104 bit information containing maximum of 8 error
* offset informations of 13 bits each. calc_ecc is copied into a
* uint64_t array and error offset indexes are populated in err_idx
* array
*/
for (i = 0; i < 8; i++) {
if (i == 4) {
err_idx[4] = ((ecc_data[1] & 0x1) << 12) | ecc_data[0];
ecc_data[1] >>= 1;
continue;
}
err_idx[i] = (ecc_data[i/4] & 0x1FFF);
ecc_data[i/4] >>= 13;
}
num_err = (readl(&regs->bank_regs[bank].sts) >> 10) & 0xF;
if (num_err == 0xF)
return -EBADMSG;
i = 0;
while (num_err--) {
change_bit(0, (unsigned long *)&err_idx[i]);
change_bit(1, (unsigned long *)&err_idx[i]);
if (err_idx[i] <= 512 * 8) {
change_bit(err_idx[i], (unsigned long *)dat);
i++;
}
}
return i;
}
/*
* fsmc_nand_probe - Probe function
* @pdev: platform device structure
*/
static int __init fsmc_nand_probe(struct platform_device *pdev)
{
struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct fsmc_nand_data *host;
struct mtd_info *mtd;
struct nand_chip *nand;
struct fsmc_regs *regs;
struct resource *res;
int nr_parts, ret = 0;
if (!pdata) {
dev_err(&pdev->dev, "platform data is NULL\n");
return -EINVAL;
}
/* Allocate memory for the device structure (and zero it) */
host = kzalloc(sizeof(*host), GFP_KERNEL);
if (!host) {
dev_err(&pdev->dev, "failed to allocate device structure\n");
return -ENOMEM;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
if (!res) {
ret = -EIO;
goto err_probe1;
}
host->resdata = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!host->resdata) {
ret = -EIO;
goto err_probe1;
}
host->data_va = ioremap(res->start, resource_size(res));
if (!host->data_va) {
ret = -EIO;
goto err_probe1;
}
host->resaddr = request_mem_region(res->start + PLAT_NAND_ALE,
resource_size(res), pdev->name);
if (!host->resaddr) {
ret = -EIO;
goto err_probe1;
}
host->addr_va = ioremap(res->start + PLAT_NAND_ALE, resource_size(res));
if (!host->addr_va) {
ret = -EIO;
goto err_probe1;
}
host->rescmd = request_mem_region(res->start + PLAT_NAND_CLE,
resource_size(res), pdev->name);
if (!host->rescmd) {
ret = -EIO;
goto err_probe1;
}
host->cmd_va = ioremap(res->start + PLAT_NAND_CLE, resource_size(res));
if (!host->cmd_va) {
ret = -EIO;
goto err_probe1;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs");
if (!res) {
ret = -EIO;
goto err_probe1;
}
host->resregs = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!host->resregs) {
ret = -EIO;
goto err_probe1;
}
host->regs_va = ioremap(res->start, resource_size(res));
if (!host->regs_va) {
ret = -EIO;
goto err_probe1;
}
host->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "failed to fetch block clock\n");
ret = PTR_ERR(host->clk);
host->clk = NULL;
goto err_probe1;
}
ret = clk_enable(host->clk);
if (ret)
goto err_probe1;
host->bank = pdata->bank;
host->select_chip = pdata->select_bank;
regs = host->regs_va;
/* Link all private pointers */
mtd = &host->mtd;
nand = &host->nand;
mtd->priv = nand;
nand->priv = host;
host->mtd.owner = THIS_MODULE;
nand->IO_ADDR_R = host->data_va;
nand->IO_ADDR_W = host->data_va;
nand->cmd_ctrl = fsmc_cmd_ctrl;
nand->chip_delay = 30;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.hwctl = fsmc_enable_hwecc;
nand->ecc.size = 512;
nand->options = pdata->options;
nand->select_chip = fsmc_select_chip;
if (pdata->width == FSMC_NAND_BW16)
nand->options |= NAND_BUSWIDTH_16;
fsmc_nand_setup(regs, host->bank, nand->options & NAND_BUSWIDTH_16);
if (get_fsmc_version(host->regs_va) == FSMC_VER8) {
nand->ecc.read_page = fsmc_read_page_hwecc;
nand->ecc.calculate = fsmc_read_hwecc_ecc4;
nand->ecc.correct = fsmc_correct_data;
nand->ecc.bytes = 13;
} else {
nand->ecc.calculate = fsmc_read_hwecc_ecc1;
nand->ecc.correct = nand_correct_data;
nand->ecc.bytes = 3;
}
/*
* Scan to find existance of the device
*/
if (nand_scan_ident(&host->mtd, 1, NULL)) {
ret = -ENXIO;
dev_err(&pdev->dev, "No NAND Device found!\n");
goto err_probe;
}
if (get_fsmc_version(host->regs_va) == FSMC_VER8) {
if (host->mtd.writesize == 512) {
nand->ecc.layout = &fsmc_ecc4_sp_layout;
host->ecc_place = &fsmc_ecc4_sp_place;
} else {
nand->ecc.layout = &fsmc_ecc4_lp_layout;
host->ecc_place = &fsmc_ecc4_lp_place;
}
} else {
nand->ecc.layout = &fsmc_ecc1_layout;
}
/* Second stage of scan to fill MTD data-structures */
if (nand_scan_tail(&host->mtd)) {
ret = -ENXIO;
goto err_probe;
}
/*
* The partition information can is accessed by (in the same precedence)
*
* command line through Bootloader,
* platform data,
* default partition information present in driver.
*/
#ifdef CONFIG_MTD_PARTITIONS
#ifdef CONFIG_MTD_CMDLINE_PARTS
/*
* Check if partition info passed via command line
*/
host->mtd.name = "nand";
nr_parts = parse_mtd_partitions(&host->mtd, part_probes,
&host->partitions, 0);
if (nr_parts > 0) {
host->nr_partitions = nr_parts;
} else {
#endif
/*
* Check if partition info passed via command line
*/
if (pdata->partitions) {
host->partitions = pdata->partitions;
host->nr_partitions = pdata->nr_partitions;
} else {
struct mtd_partition *partition;
int i;
/* Select the default partitions info */
switch (host->mtd.size) {
case 0x01000000:
case 0x02000000:
case 0x04000000:
host->partitions = partition_info_16KB_blk;
host->nr_partitions =
sizeof(partition_info_16KB_blk) /
sizeof(struct mtd_partition);
break;
case 0x08000000:
case 0x10000000:
case 0x20000000:
case 0x40000000:
host->partitions = partition_info_128KB_blk;
host->nr_partitions =
sizeof(partition_info_128KB_blk) /
sizeof(struct mtd_partition);
break;
default:
ret = -ENXIO;
pr_err("Unsupported NAND size\n");
goto err_probe;
}
partition = host->partitions;
for (i = 0; i < host->nr_partitions; i++, partition++) {
if (partition->size == 0) {
partition->size = host->mtd.size -
partition->offset;
break;
}
}
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
}
#endif
if (host->partitions) {
ret = add_mtd_partitions(&host->mtd, host->partitions,
host->nr_partitions);
if (ret)
goto err_probe;
}
#else
dev_info(&pdev->dev, "Registering %s as whole device\n", mtd->name);
if (!add_mtd_device(mtd)) {
ret = -ENXIO;
goto err_probe;
}
#endif
platform_set_drvdata(pdev, host);
dev_info(&pdev->dev, "FSMC NAND driver registration successful\n");
return 0;
err_probe:
clk_disable(host->clk);
err_probe1:
if (host->clk)
clk_put(host->clk);
if (host->regs_va)
iounmap(host->regs_va);
if (host->resregs)
release_mem_region(host->resregs->start,
resource_size(host->resregs));
if (host->cmd_va)
iounmap(host->cmd_va);
if (host->rescmd)
release_mem_region(host->rescmd->start,
resource_size(host->rescmd));
if (host->addr_va)
iounmap(host->addr_va);
if (host->resaddr)
release_mem_region(host->resaddr->start,
resource_size(host->resaddr));
if (host->data_va)
iounmap(host->data_va);
if (host->resdata)
release_mem_region(host->resdata->start,
resource_size(host->resdata));
kfree(host);
return ret;
}
/*
* Clean up routine
*/
static int fsmc_nand_remove(struct platform_device *pdev)
{
struct fsmc_nand_data *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (host) {
#ifdef CONFIG_MTD_PARTITIONS
del_mtd_partitions(&host->mtd);
#else
del_mtd_device(&host->mtd);
#endif
clk_disable(host->clk);
clk_put(host->clk);
iounmap(host->regs_va);
release_mem_region(host->resregs->start,
resource_size(host->resregs));
iounmap(host->cmd_va);
release_mem_region(host->rescmd->start,
resource_size(host->rescmd));
iounmap(host->addr_va);
release_mem_region(host->resaddr->start,
resource_size(host->resaddr));
iounmap(host->data_va);
release_mem_region(host->resdata->start,
resource_size(host->resdata));
kfree(host);
}
return 0;
}
#ifdef CONFIG_PM
static int fsmc_nand_suspend(struct device *dev)
{
struct fsmc_nand_data *host = dev_get_drvdata(dev);
if (host)
clk_disable(host->clk);
return 0;
}
static int fsmc_nand_resume(struct device *dev)
{
struct fsmc_nand_data *host = dev_get_drvdata(dev);
if (host)
clk_enable(host->clk);
return 0;
}
static const struct dev_pm_ops fsmc_nand_pm_ops = {
.suspend = fsmc_nand_suspend,
.resume = fsmc_nand_resume,
};
#endif
static struct platform_driver fsmc_nand_driver = {
.remove = fsmc_nand_remove,
.driver = {
.owner = THIS_MODULE,
.name = "fsmc-nand",
#ifdef CONFIG_PM
.pm = &fsmc_nand_pm_ops,
#endif
},
};
static int __init fsmc_nand_init(void)
{
return platform_driver_probe(&fsmc_nand_driver,
fsmc_nand_probe);
}
module_init(fsmc_nand_init);
static void __exit fsmc_nand_exit(void)
{
platform_driver_unregister(&fsmc_nand_driver);
}
module_exit(fsmc_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
MODULE_DESCRIPTION("NAND driver for SPEAr Platforms");

View File

@ -568,6 +568,7 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
uint rcw_width; uint rcw_width;
uint rcwh; uint rcwh;
uint romloc, ps; uint romloc, ps;
int ret = 0;
rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
if (!rmnode) { if (!rmnode) {
@ -579,7 +580,8 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
rm = of_iomap(rmnode, 0); rm = of_iomap(rmnode, 0);
if (!rm) { if (!rm) {
dev_err(prv->dev, "Error mapping reset module node!\n"); dev_err(prv->dev, "Error mapping reset module node!\n");
return -EBUSY; ret = -EBUSY;
goto out;
} }
rcwh = in_be32(&rm->rcwhr); rcwh = in_be32(&rm->rcwhr);
@ -628,8 +630,9 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
rcw_width * 8, rcw_pagesize, rcw_width * 8, rcw_pagesize,
rcw_sparesize); rcw_sparesize);
iounmap(rm); iounmap(rm);
out:
of_node_put(rmnode); of_node_put(rmnode);
return 0; return ret;
} }
/* Free driver resources */ /* Free driver resources */
@ -660,7 +663,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
#endif #endif
struct nand_chip *chip; struct nand_chip *chip;
unsigned long regs_paddr, regs_size; unsigned long regs_paddr, regs_size;
const uint *chips_no; const __be32 *chips_no;
int resettime = 0; int resettime = 0;
int retval = 0; int retval = 0;
int rev, len; int rev, len;
@ -803,7 +806,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
} }
/* Detect NAND chips */ /* Detect NAND chips */
if (nand_scan(mtd, *chips_no)) { if (nand_scan(mtd, be32_to_cpup(chips_no))) {
dev_err(dev, "NAND Flash not found !\n"); dev_err(dev, "NAND Flash not found !\n");
devm_free_irq(dev, prv->irq, mtd); devm_free_irq(dev, prv->irq, mtd);
retval = -ENXIO; retval = -ENXIO;

View File

@ -45,7 +45,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/leds.h> #include <linux/leds.h>
#include <asm/io.h> #include <linux/io.h>
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
@ -59,7 +59,7 @@ static struct nand_ecclayout nand_oob_8 = {
{.offset = 3, {.offset = 3,
.length = 2}, .length = 2},
{.offset = 6, {.offset = 6,
.length = 2}} .length = 2} }
}; };
static struct nand_ecclayout nand_oob_16 = { static struct nand_ecclayout nand_oob_16 = {
@ -67,7 +67,7 @@ static struct nand_ecclayout nand_oob_16 = {
.eccpos = {0, 1, 2, 3, 6, 7}, .eccpos = {0, 1, 2, 3, 6, 7},
.oobfree = { .oobfree = {
{.offset = 8, {.offset = 8,
. length = 8}} . length = 8} }
}; };
static struct nand_ecclayout nand_oob_64 = { static struct nand_ecclayout nand_oob_64 = {
@ -78,7 +78,7 @@ static struct nand_ecclayout nand_oob_64 = {
56, 57, 58, 59, 60, 61, 62, 63}, 56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = { .oobfree = {
{.offset = 2, {.offset = 2,
.length = 38}} .length = 38} }
}; };
static struct nand_ecclayout nand_oob_128 = { static struct nand_ecclayout nand_oob_128 = {
@ -92,7 +92,7 @@ static struct nand_ecclayout nand_oob_128 = {
120, 121, 122, 123, 124, 125, 126, 127}, 120, 121, 122, 123, 124, 125, 126, 127},
.oobfree = { .oobfree = {
{.offset = 2, {.offset = 2,
.length = 78}} .length = 78} }
}; };
static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
@ -612,7 +612,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
NAND_CTRL_CLE | NAND_CTRL_CHANGE); NAND_CTRL_CLE | NAND_CTRL_CHANGE);
chip->cmd_ctrl(mtd, chip->cmd_ctrl(mtd,
NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
;
return; return;
/* This applies to read commands */ /* This applies to read commands */
@ -718,7 +719,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip->cmd_ctrl(mtd, NAND_CMD_NONE, chip->cmd_ctrl(mtd, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE); NAND_NCE | NAND_CTRL_CHANGE);
while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ; while (!(chip->read_byte(mtd) & NAND_STATUS_READY))
;
return; return;
case NAND_CMD_RNDOUT: case NAND_CMD_RNDOUT:
@ -784,7 +786,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
spinlock_t *lock = &chip->controller->lock; spinlock_t *lock = &chip->controller->lock;
wait_queue_head_t *wq = &chip->controller->wq; wait_queue_head_t *wq = &chip->controller->wq;
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
retry: retry:
spin_lock(lock); spin_lock(lock);
/* Hardware controller shared among independent devices */ /* Hardware controller shared among independent devices */
@ -834,7 +836,7 @@ static void panic_nand_wait(struct mtd_info *mtd, struct nand_chip *chip,
break; break;
} }
mdelay(1); mdelay(1);
} }
} }
/** /**
@ -980,6 +982,7 @@ out:
return ret; return ret;
} }
EXPORT_SYMBOL(nand_unlock);
/** /**
* nand_lock - [REPLACEABLE] locks all blocks present in the device * nand_lock - [REPLACEABLE] locks all blocks present in the device
@ -1049,6 +1052,7 @@ out:
return ret; return ret;
} }
EXPORT_SYMBOL(nand_lock);
/** /**
* nand_read_page_raw - [Intern] read raw page data without ecc * nand_read_page_raw - [Intern] read raw page data without ecc
@ -1076,8 +1080,9 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
* *
* We need a special oob layout and handling even when OOB isn't used. * We need a special oob layout and handling even when OOB isn't used.
*/ */
static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, static int nand_read_page_raw_syndrome(struct mtd_info *mtd,
uint8_t *buf, int page) struct nand_chip *chip,
uint8_t *buf, int page)
{ {
int eccsize = chip->ecc.size; int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -1158,7 +1163,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @readlen: data length * @readlen: data length
* @bufpoi: buffer to store read data * @bufpoi: buffer to store read data
*/ */
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
{ {
int start_step, end_step, num_steps; int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos; uint32_t *eccpos = chip->ecc.layout->eccpos;
@ -1166,6 +1172,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
int data_col_addr, i, gaps = 0; int data_col_addr, i, gaps = 0;
int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
int index = 0;
/* Column address wihin the page aligned to ECC size (256bytes). */ /* Column address wihin the page aligned to ECC size (256bytes). */
start_step = data_offs / chip->ecc.size; start_step = data_offs / chip->ecc.size;
@ -1204,26 +1211,30 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
} else { } else {
/* send the command to read the particular ecc bytes */ /* send the command to read the particular ecc bytes */
/* take care about buswidth alignment in read_buf */ /* take care about buswidth alignment in read_buf */
aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); index = start_step * chip->ecc.bytes;
aligned_pos = eccpos[index] & ~(busw - 1);
aligned_len = eccfrag_len; aligned_len = eccfrag_len;
if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) if (eccpos[index] & (busw - 1))
aligned_len++; aligned_len++;
if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) if (eccpos[index + (num_steps * chip->ecc.bytes)] & (busw - 1))
aligned_len++; aligned_len++;
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
mtd->writesize + aligned_pos, -1);
chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
} }
for (i = 0; i < eccfrag_len; i++) for (i = 0; i < eccfrag_len; i++)
chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + index]];
p = bufpoi + data_col_addr; p = bufpoi + data_col_addr;
for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
int stat; int stat;
stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); stat = chip->ecc.correct(mtd, p,
if (stat == -1) &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
if (stat < 0)
mtd->ecc_stats.failed++; mtd->ecc_stats.failed++;
else else
mtd->ecc_stats.corrected += stat; mtd->ecc_stats.corrected += stat;
@ -1390,7 +1401,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
struct mtd_oob_ops *ops, size_t len) struct mtd_oob_ops *ops, size_t len)
{ {
switch(ops->mode) { switch (ops->mode) {
case MTD_OOB_PLACE: case MTD_OOB_PLACE:
case MTD_OOB_RAW: case MTD_OOB_RAW:
@ -1402,7 +1413,7 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
uint32_t boffs = 0, roffs = ops->ooboffs; uint32_t boffs = 0, roffs = ops->ooboffs;
size_t bytes = 0; size_t bytes = 0;
for(; free->length && len; free++, len -= bytes) { for (; free->length && len; free++, len -= bytes) {
/* Read request not from offset 0 ? */ /* Read request not from offset 0 ? */
if (unlikely(roffs)) { if (unlikely(roffs)) {
if (roffs >= free->length) { if (roffs >= free->length) {
@ -1466,7 +1477,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
buf = ops->datbuf; buf = ops->datbuf;
oob = ops->oobbuf; oob = ops->oobbuf;
while(1) { while (1) {
bytes = min(mtd->writesize - col, readlen); bytes = min(mtd->writesize - col, readlen);
aligned = (bytes == mtd->writesize); aligned = (bytes == mtd->writesize);
@ -1484,7 +1495,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
ret = chip->ecc.read_page_raw(mtd, chip, ret = chip->ecc.read_page_raw(mtd, chip,
bufpoi, page); bufpoi, page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); ret = chip->ecc.read_subpage(mtd, chip,
col, bytes, bufpoi);
else else
ret = chip->ecc.read_page(mtd, chip, bufpoi, ret = chip->ecc.read_page(mtd, chip, bufpoi,
page); page);
@ -1493,7 +1505,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Transfer not aligned data */ /* Transfer not aligned data */
if (!aligned) { if (!aligned) {
if (!NAND_SUBPAGE_READ(chip) && !oob) if (!NAND_SUBPAGE_READ(chip) && !oob &&
!(mtd->ecc_stats.failed - stats.failed))
chip->pagebuf = realpage; chip->pagebuf = realpage;
memcpy(buf, chip->buffers->databuf + col, bytes); memcpy(buf, chip->buffers->databuf + col, bytes);
} }
@ -1791,7 +1804,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
realpage = (int)(from >> chip->page_shift); realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask; page = realpage & chip->pagemask;
while(1) { while (1) {
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
len = min(len, readlen); len = min(len, readlen);
@ -1861,7 +1874,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
nand_get_device(chip, mtd, FL_READING); nand_get_device(chip, mtd, FL_READING);
switch(ops->mode) { switch (ops->mode) {
case MTD_OOB_PLACE: case MTD_OOB_PLACE:
case MTD_OOB_AUTO: case MTD_OOB_AUTO:
case MTD_OOB_RAW: case MTD_OOB_RAW:
@ -1876,7 +1889,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
else else
ret = nand_do_read_ops(mtd, from, ops); ret = nand_do_read_ops(mtd, from, ops);
out: out:
nand_release_device(mtd); nand_release_device(mtd);
return ret; return ret;
} }
@ -1905,8 +1918,9 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
* *
* We need a special oob layout and handling even when ECC isn't checked. * We need a special oob layout and handling even when ECC isn't checked.
*/ */
static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
const uint8_t *buf) struct nand_chip *chip,
const uint8_t *buf)
{ {
int eccsize = chip->ecc.size; int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes; int eccbytes = chip->ecc.bytes;
@ -2099,7 +2113,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
switch(ops->mode) { switch (ops->mode) {
case MTD_OOB_PLACE: case MTD_OOB_PLACE:
case MTD_OOB_RAW: case MTD_OOB_RAW:
@ -2111,7 +2125,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
uint32_t boffs = 0, woffs = ops->ooboffs; uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0; size_t bytes = 0;
for(; free->length && len; free++, len -= bytes) { for (; free->length && len; free++, len -= bytes) {
/* Write request not from offset 0 ? */ /* Write request not from offset 0 ? */
if (unlikely(woffs)) { if (unlikely(woffs)) {
if (woffs >= free->length) { if (woffs >= free->length) {
@ -2137,7 +2151,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
return NULL; return NULL;
} }
#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 #define NOTALIGNED(x) ((x & (chip->subpagesize - 1)) != 0)
/** /**
* nand_do_write_ops - [Internal] NAND write with ECC * nand_do_write_ops - [Internal] NAND write with ECC
@ -2200,10 +2214,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
memset(chip->oob_poi, 0xff, mtd->oobsize); memset(chip->oob_poi, 0xff, mtd->oobsize);
/* Don't allow multipage oob writes with offset */ /* Don't allow multipage oob writes with offset */
if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
return -EINVAL; return -EINVAL;
while(1) { while (1) {
int bytes = mtd->writesize; int bytes = mtd->writesize;
int cached = writelen > bytes && page != blockmask; int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf; uint8_t *wbuf = buf;
@ -2431,7 +2445,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
nand_get_device(chip, mtd, FL_WRITING); nand_get_device(chip, mtd, FL_WRITING);
switch(ops->mode) { switch (ops->mode) {
case MTD_OOB_PLACE: case MTD_OOB_PLACE:
case MTD_OOB_AUTO: case MTD_OOB_AUTO:
case MTD_OOB_RAW: case MTD_OOB_RAW:
@ -2446,7 +2460,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
else else
ret = nand_do_write_ops(mtd, to, ops); ret = nand_do_write_ops(mtd, to, ops);
out: out:
nand_release_device(mtd); nand_release_device(mtd);
return ret; return ret;
} }
@ -2511,7 +2525,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
{ {
int page, status, pages_per_block, ret, chipnr; int page, status, pages_per_block, ret, chipnr;
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
loff_t rewrite_bbt[NAND_MAX_CHIPS]={0}; loff_t rewrite_bbt[NAND_MAX_CHIPS] = {0};
unsigned int bbt_masked_page = 0xffffffff; unsigned int bbt_masked_page = 0xffffffff;
loff_t len; loff_t len;
@ -2632,7 +2646,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
} }
instr->state = MTD_ERASE_DONE; instr->state = MTD_ERASE_DONE;
erase_exit: erase_exit:
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
@ -2706,7 +2720,8 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
int ret; int ret;
if ((ret = nand_block_isbad(mtd, ofs))) { ret = nand_block_isbad(mtd, ofs);
if (ret) {
/* If it was bad already, return success and do nothing. */ /* If it was bad already, return success and do nothing. */
if (ret > 0) if (ret > 0)
return 0; return 0;
@ -2786,16 +2801,116 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
} }
/*
* sanitize ONFI strings so we can safely print them
*/
static void sanitize_string(uint8_t *s, size_t len)
{
ssize_t i;
/* null terminate */
s[len - 1] = 0;
/* remove non printable chars */
for (i = 0; i < len - 1; i++) {
if (s[i] < ' ' || s[i] > 127)
s[i] = '?';
}
/* remove trailing spaces */
strim(s);
}
static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
{
int i;
while (len--) {
crc ^= *p++ << 8;
for (i = 0; i < 8; i++)
crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
}
return crc;
}
/*
* Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
*/
static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
int busw)
{
struct nand_onfi_params *p = &chip->onfi_params;
int i;
int val;
/* try ONFI for unknow chip or LP */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
return 0;
printk(KERN_INFO "ONFI flash detected\n");
chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
for (i = 0; i < 3; i++) {
chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
le16_to_cpu(p->crc)) {
printk(KERN_INFO "ONFI param page %d valid\n", i);
break;
}
}
if (i == 3)
return 0;
/* check version */
val = le16_to_cpu(p->revision);
if (val == 1 || val > (1 << 4)) {
printk(KERN_INFO "%s: unsupported ONFI version: %d\n",
__func__, val);
return 0;
}
if (val & (1 << 4))
chip->onfi_version = 22;
else if (val & (1 << 3))
chip->onfi_version = 21;
else if (val & (1 << 2))
chip->onfi_version = 20;
else
chip->onfi_version = 10;
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
sanitize_string(p->model, sizeof(p->model));
if (!mtd->name)
mtd->name = p->model;
mtd->writesize = le32_to_cpu(p->byte_per_page);
mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
chip->chipsize = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
busw = 0;
if (le16_to_cpu(p->features) & 1)
busw = NAND_BUSWIDTH_16;
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= (NAND_NO_READRDY |
NAND_NO_AUTOINCR) & NAND_CHIPOPTIONS_MSK;
return 1;
}
/* /*
* Get the flash and manufacturer id and lookup if the type is supported * Get the flash and manufacturer id and lookup if the type is supported
*/ */
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
struct nand_chip *chip, struct nand_chip *chip,
int busw, int *maf_id, int busw,
int *maf_id, int *dev_id,
struct nand_flash_dev *type) struct nand_flash_dev *type)
{ {
int i, dev_id, maf_idx; int i, maf_idx;
u8 id_data[8]; u8 id_data[8];
int ret;
/* Select the device */ /* Select the device */
chip->select_chip(mtd, 0); chip->select_chip(mtd, 0);
@ -2811,7 +2926,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/* Read manufacturer and device IDs */ /* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd); *maf_id = chip->read_byte(mtd);
dev_id = chip->read_byte(mtd); *dev_id = chip->read_byte(mtd);
/* Try again to make sure, as some systems the bus-hold or other /* Try again to make sure, as some systems the bus-hold or other
* interface concerns can cause random data which looks like a * interface concerns can cause random data which looks like a
@ -2821,15 +2936,13 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */ for (i = 0; i < 2; i++)
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd); id_data[i] = chip->read_byte(mtd);
if (id_data[0] != *maf_id || id_data[1] != dev_id) { if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
printk(KERN_INFO "%s: second ID read did not match " printk(KERN_INFO "%s: second ID read did not match "
"%02x,%02x against %02x,%02x\n", __func__, "%02x,%02x against %02x,%02x\n", __func__,
*maf_id, dev_id, id_data[0], id_data[1]); *maf_id, *dev_id, id_data[0], id_data[1]);
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
} }
@ -2837,8 +2950,23 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
type = nand_flash_ids; type = nand_flash_ids;
for (; type->name != NULL; type++) for (; type->name != NULL; type++)
if (dev_id == type->id) if (*dev_id == type->id)
break; break;
chip->onfi_version = 0;
if (!type->name || !type->pagesize) {
/* Check is chip is ONFI compliant */
ret = nand_flash_detect_onfi(mtd, chip, busw);
if (ret)
goto ident_done;
}
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read entire ID string */
for (i = 0; i < 8; i++)
id_data[i] = chip->read_byte(mtd);
if (!type->name) if (!type->name)
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
@ -2848,8 +2976,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
chip->chipsize = (uint64_t)type->chipsize << 20; chip->chipsize = (uint64_t)type->chipsize << 20;
/* Newer devices have all the information in additional id bytes */ if (!type->pagesize && chip->init_size) {
if (!type->pagesize) { /* set the pagesize, oobsize, erasesize by the driver*/
busw = chip->init_size(mtd, chip, id_data);
} else if (!type->pagesize) {
int extid; int extid;
/* The 3rd id byte holds MLC / multichip data */ /* The 3rd id byte holds MLC / multichip data */
chip->cellinfo = id_data[2]; chip->cellinfo = id_data[2];
@ -2859,7 +2989,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
/* /*
* Field definitions are in the following datasheets: * Field definitions are in the following datasheets:
* Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
* New style (6 byte ID): Samsung K9GAG08U0D (p.40) * New style (6 byte ID): Samsung K9GBG08U0M (p.40)
* *
* Check for wraparound + Samsung ID + nonzero 6th byte * Check for wraparound + Samsung ID + nonzero 6th byte
* to decide what to do. * to decide what to do.
@ -2872,7 +3002,20 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
mtd->writesize = 2048 << (extid & 0x03); mtd->writesize = 2048 << (extid & 0x03);
extid >>= 2; extid >>= 2;
/* Calc oobsize */ /* Calc oobsize */
mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218; switch (extid & 0x03) {
case 1:
mtd->oobsize = 128;
break;
case 2:
mtd->oobsize = 218;
break;
case 3:
mtd->oobsize = 400;
break;
default:
mtd->oobsize = 436;
break;
}
extid >>= 2; extid >>= 2;
/* Calc blocksize */ /* Calc blocksize */
mtd->erasesize = (128 * 1024) << mtd->erasesize = (128 * 1024) <<
@ -2900,7 +3043,35 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
mtd->writesize = type->pagesize; mtd->writesize = type->pagesize;
mtd->oobsize = mtd->writesize / 32; mtd->oobsize = mtd->writesize / 32;
busw = type->options & NAND_BUSWIDTH_16; busw = type->options & NAND_BUSWIDTH_16;
/*
* Check for Spansion/AMD ID + repeating 5th, 6th byte since
* some Spansion chips have erasesize that conflicts with size
* listed in nand_ids table
* Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
*/
if (*maf_id == NAND_MFR_AMD && id_data[4] != 0x00 &&
id_data[5] == 0x00 && id_data[6] == 0x00 &&
id_data[7] == 0x00 && mtd->writesize == 512) {
mtd->erasesize = 128 * 1024;
mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
}
} }
/* Get chip options, preserve non chip based options */
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
ident_done:
/*
* Set chip as a default. Board drivers can override it, if necessary
*/
chip->options |= NAND_NO_AUTOINCR;
/* Try to identify manufacturer */ /* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
@ -2915,7 +3086,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if (busw != (chip->options & NAND_BUSWIDTH_16)) { if (busw != (chip->options & NAND_BUSWIDTH_16)) {
printk(KERN_INFO "NAND device: Manufacturer ID:" printk(KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
dev_id, nand_manuf_ids[maf_idx].name, mtd->name); *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
printk(KERN_WARNING "NAND bus width %d instead %d bit\n", printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
(chip->options & NAND_BUSWIDTH_16) ? 16 : 8, (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8); busw ? 16 : 8);
@ -2931,8 +3102,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
ffs(mtd->erasesize) - 1; ffs(mtd->erasesize) - 1;
if (chip->chipsize & 0xffffffff) if (chip->chipsize & 0xffffffff)
chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
else else {
chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 32 - 1; chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32));
chip->chip_shift += 32 - 1;
}
/* Set the bad block position */ /* Set the bad block position */
if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16)) if (mtd->writesize > 512 || (busw & NAND_BUSWIDTH_16))
@ -2940,27 +3113,12 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
else else
chip->badblockpos = NAND_SMALL_BADBLOCK_POS; chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
/* Get chip options, preserve non chip based options */
chip->options &= ~NAND_CHIPOPTIONS_MSK;
chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
/*
* Set chip as a default. Board drivers can override it, if necessary
*/
chip->options |= NAND_NO_AUTOINCR;
/* Check if chip is a not a samsung device. Do not clear the
* options for chips which are not having an extended id.
*/
if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
/* /*
* Bad block marker is stored in the last page of each block * Bad block marker is stored in the last page of each block
* on Samsung and Hynix MLC devices; stored in first two pages * on Samsung and Hynix MLC devices; stored in first two pages
* of each block on Micron devices with 2KiB pages and on * of each block on Micron devices with 2KiB pages and on
* SLC Samsung, Hynix, and AMD/Spansion. All others scan only * SLC Samsung, Hynix, Toshiba and AMD/Spansion. All others scan
* the first page. * only the first page.
*/ */
if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(*maf_id == NAND_MFR_SAMSUNG || (*maf_id == NAND_MFR_SAMSUNG ||
@ -2969,6 +3127,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) &&
(*maf_id == NAND_MFR_SAMSUNG || (*maf_id == NAND_MFR_SAMSUNG ||
*maf_id == NAND_MFR_HYNIX || *maf_id == NAND_MFR_HYNIX ||
*maf_id == NAND_MFR_TOSHIBA ||
*maf_id == NAND_MFR_AMD)) || *maf_id == NAND_MFR_AMD)) ||
(mtd->writesize == 2048 && (mtd->writesize == 2048 &&
*maf_id == NAND_MFR_MICRON)) *maf_id == NAND_MFR_MICRON))
@ -2994,9 +3153,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
if (mtd->writesize > 512 && chip->cmdfunc == nand_command) if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
chip->cmdfunc = nand_command_lp; chip->cmdfunc = nand_command_lp;
/* TODO onfi flash name */
printk(KERN_INFO "NAND device: Manufacturer ID:" printk(KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
nand_manuf_ids[maf_idx].name, type->name); nand_manuf_ids[maf_idx].name,
chip->onfi_version ? type->name : chip->onfi_params.model);
return type; return type;
} }
@ -3015,7 +3176,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
int nand_scan_ident(struct mtd_info *mtd, int maxchips, int nand_scan_ident(struct mtd_info *mtd, int maxchips,
struct nand_flash_dev *table) struct nand_flash_dev *table)
{ {
int i, busw, nand_maf_id; int i, busw, nand_maf_id, nand_dev_id;
struct nand_chip *chip = mtd->priv; struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type; struct nand_flash_dev *type;
@ -3025,7 +3186,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
nand_set_defaults(chip, busw); nand_set_defaults(chip, busw);
/* Read the flash type */ /* Read the flash type */
type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table); type = nand_get_flash_type(mtd, chip, busw,
&nand_maf_id, &nand_dev_id, table);
if (IS_ERR(type)) { if (IS_ERR(type)) {
if (!(chip->options & NAND_SCAN_SILENT_NODEV)) if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@ -3043,7 +3205,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */ /* Read manufacturer and device IDs */
if (nand_maf_id != chip->read_byte(mtd) || if (nand_maf_id != chip->read_byte(mtd) ||
type->id != chip->read_byte(mtd)) nand_dev_id != chip->read_byte(mtd))
break; break;
} }
if (i > 1) if (i > 1)
@ -3055,6 +3217,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
return 0; return 0;
} }
EXPORT_SYMBOL(nand_scan_ident);
/** /**
@ -3219,7 +3382,7 @@ int nand_scan_tail(struct mtd_info *mtd)
* mode * mode
*/ */
chip->ecc.steps = mtd->writesize / chip->ecc.size; chip->ecc.steps = mtd->writesize / chip->ecc.size;
if(chip->ecc.steps * chip->ecc.size != mtd->writesize) { if (chip->ecc.steps * chip->ecc.size != mtd->writesize) {
printk(KERN_WARNING "Invalid ecc parameters\n"); printk(KERN_WARNING "Invalid ecc parameters\n");
BUG(); BUG();
} }
@ -3231,7 +3394,7 @@ int nand_scan_tail(struct mtd_info *mtd)
*/ */
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
!(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
switch(chip->ecc.steps) { switch (chip->ecc.steps) {
case 2: case 2:
mtd->subpage_sft = 1; mtd->subpage_sft = 1;
break; break;
@ -3283,10 +3446,11 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Build bad block table */ /* Build bad block table */
return chip->scan_bbt(mtd); return chip->scan_bbt(mtd);
} }
EXPORT_SYMBOL(nand_scan_tail);
/* is_module_text_address() isn't exported, and it's mostly a pointless /* is_module_text_address() isn't exported, and it's mostly a pointless
test if this is a module _anyway_ -- they'd have to try _really_ hard * test if this is a module _anyway_ -- they'd have to try _really_ hard
to call us from in-kernel code if the core NAND support is modular. */ * to call us from in-kernel code if the core NAND support is modular. */
#ifdef MODULE #ifdef MODULE
#define caller_is_module() (1) #define caller_is_module() (1)
#else #else
@ -3322,6 +3486,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
ret = nand_scan_tail(mtd); ret = nand_scan_tail(mtd);
return ret; return ret;
} }
EXPORT_SYMBOL(nand_scan);
/** /**
* nand_release - [NAND Interface] Free resources held by the NAND device * nand_release - [NAND Interface] Free resources held by the NAND device
@ -3348,12 +3513,6 @@ void nand_release(struct mtd_info *mtd)
& NAND_BBT_DYNAMICSTRUCT) & NAND_BBT_DYNAMICSTRUCT)
kfree(chip->badblock_pattern); kfree(chip->badblock_pattern);
} }
EXPORT_SYMBOL_GPL(nand_lock);
EXPORT_SYMBOL_GPL(nand_unlock);
EXPORT_SYMBOL_GPL(nand_scan);
EXPORT_SYMBOL_GPL(nand_scan_ident);
EXPORT_SYMBOL_GPL(nand_scan_tail);
EXPORT_SYMBOL_GPL(nand_release); EXPORT_SYMBOL_GPL(nand_release);
static int __init nand_base_init(void) static int __init nand_base_init(void)
@ -3371,5 +3530,6 @@ module_init(nand_base_init);
module_exit(nand_base_exit); module_exit(nand_base_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>"); MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION("Generic NAND flash driver code"); MODULE_DESCRIPTION("Generic NAND flash driver code");

View File

@ -13,28 +13,37 @@
* Description: * Description:
* *
* When nand_scan_bbt is called, then it tries to find the bad block table * When nand_scan_bbt is called, then it tries to find the bad block table
* depending on the options in the bbt descriptor(s). If a bbt is found * depending on the options in the BBT descriptor(s). If no flash based BBT
* then the contents are read and the memory based bbt is created. If a * (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
* mirrored bbt is selected then the mirror is searched too and the * marked good / bad blocks. This information is used to create a memory BBT.
* versions are compared. If the mirror has a greater version number * Once a new bad block is discovered then the "factory" information is updated
* than the mirror bbt is used to build the memory based bbt. * on the device.
* If a flash based BBT is specified then the function first tries to find the
* BBT on flash. If a BBT is found then the contents are read and the memory
* based BBT is created. If a mirrored BBT is selected then the mirror is
* searched too and the versions are compared. If the mirror has a greater
* version number than the mirror BBT is used to build the memory based BBT.
* If the tables are not versioned, then we "or" the bad block information. * If the tables are not versioned, then we "or" the bad block information.
* If one of the bbt's is out of date or does not exist it is (re)created. * If one of the BBTs is out of date or does not exist it is (re)created.
* If no bbt exists at all then the device is scanned for factory marked * If no BBT exists at all then the device is scanned for factory marked
* good / bad blocks and the bad block tables are created. * good / bad blocks and the bad block tables are created.
* *
* For manufacturer created bbts like the one found on M-SYS DOC devices * For manufacturer created BBTs like the one found on M-SYS DOC devices
* the bbt is searched and read but never created * the BBT is searched and read but never created
* *
* The autogenerated bad block table is located in the last good blocks * The auto generated bad block table is located in the last good blocks
* of the device. The table is mirrored, so it can be updated eventually. * of the device. The table is mirrored, so it can be updated eventually.
* The table is marked in the oob area with an ident pattern and a version * The table is marked in the OOB area with an ident pattern and a version
* number which indicates which of both tables is more up to date. * number which indicates which of both tables is more up to date. If the NAND
* controller needs the complete OOB area for the ECC information then the
* option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern
* and the version byte into the data area and the OOB area will remain
* untouched.
* *
* The table uses 2 bits per block * The table uses 2 bits per block
* 11b: block is good * 11b: block is good
* 00b: block is factory marked bad * 00b: block is factory marked bad
* 01b, 10b: block is marked bad due to wear * 01b, 10b: block is marked bad due to wear
* *
* The memory bad block table uses the following scheme: * The memory bad block table uses the following scheme:
* 00b: block is good * 00b: block is good
@ -59,6 +68,16 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
{
int ret;
ret = memcmp(buf, td->pattern, td->len);
if (!ret)
return ret;
return -1;
}
/** /**
* check_pattern - [GENERIC] check if a pattern is in the buffer * check_pattern - [GENERIC] check if a pattern is in the buffer
* @buf: the buffer to search * @buf: the buffer to search
@ -77,6 +96,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
int i, end = 0; int i, end = 0;
uint8_t *p = buf; uint8_t *p = buf;
if (td->options & NAND_BBT_NO_OOB)
return check_pattern_no_oob(buf, td);
end = paglen + td->offs; end = paglen + td->offs;
if (td->options & NAND_BBT_SCANEMPTY) { if (td->options & NAND_BBT_SCANEMPTY) {
for (i = 0; i < end; i++) { for (i = 0; i < end; i++) {
@ -155,33 +177,64 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
return 0; return 0;
} }
/**
* add_marker_len - compute the length of the marker in data area
* @td: BBT descriptor used for computation
*
* The length will be 0 if the markeris located in OOB area.
*/
static u32 add_marker_len(struct nand_bbt_descr *td)
{
u32 len;
if (!(td->options & NAND_BBT_NO_OOB))
return 0;
len = td->len;
if (td->options & NAND_BBT_VERSION)
len++;
return len;
}
/** /**
* read_bbt - [GENERIC] Read the bad block table starting from page * read_bbt - [GENERIC] Read the bad block table starting from page
* @mtd: MTD device structure * @mtd: MTD device structure
* @buf: temporary buffer * @buf: temporary buffer
* @page: the starting page * @page: the starting page
* @num: the number of bbt descriptors to read * @num: the number of bbt descriptors to read
* @bits: number of bits per block * @td: the bbt describtion table
* @offs: offset in the memory table * @offs: offset in the memory table
* @reserved_block_code: Pattern to identify reserved blocks
* *
* Read the bad block table starting from page. * Read the bad block table starting from page.
* *
*/ */
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
int bits, int offs, int reserved_block_code) struct nand_bbt_descr *td, int offs)
{ {
int res, i, j, act = 0; int res, i, j, act = 0;
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
size_t retlen, len, totlen; size_t retlen, len, totlen;
loff_t from; loff_t from;
int bits = td->options & NAND_BBT_NRBITS_MSK;
uint8_t msk = (uint8_t) ((1 << bits) - 1); uint8_t msk = (uint8_t) ((1 << bits) - 1);
u32 marker_len;
int reserved_block_code = td->reserved_block_code;
totlen = (num * bits) >> 3; totlen = (num * bits) >> 3;
marker_len = add_marker_len(td);
from = ((loff_t) page) << this->page_shift; from = ((loff_t) page) << this->page_shift;
while (totlen) { while (totlen) {
len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
if (marker_len) {
/*
* In case the BBT marker is not in the OOB area it
* will be just in the first page.
*/
len -= marker_len;
from += marker_len;
marker_len = 0;
}
res = mtd->read(mtd, from, len, &retlen, buf); res = mtd->read(mtd, from, len, &retlen, buf);
if (res < 0) { if (res < 0) {
if (retlen != len) { if (retlen != len) {
@ -238,30 +291,47 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
int res = 0, i; int res = 0, i;
int bits;
bits = td->options & NAND_BBT_NRBITS_MSK;
if (td->options & NAND_BBT_PERCHIP) { if (td->options & NAND_BBT_PERCHIP) {
int offs = 0; int offs = 0;
for (i = 0; i < this->numchips; i++) { for (i = 0; i < this->numchips; i++) {
if (chip == -1 || chip == i) if (chip == -1 || chip == i)
res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code); res = read_bbt(mtd, buf, td->pages[i],
this->chipsize >> this->bbt_erase_shift,
td, offs);
if (res) if (res)
return res; return res;
offs += this->chipsize >> (this->bbt_erase_shift + 2); offs += this->chipsize >> (this->bbt_erase_shift + 2);
} }
} else { } else {
res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code); res = read_bbt(mtd, buf, td->pages[0],
mtd->size >> this->bbt_erase_shift, td, 0);
if (res) if (res)
return res; return res;
} }
return 0; return 0;
} }
/*
* BBT marker is in the first page, no OOB.
*/
static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
struct nand_bbt_descr *td)
{
size_t retlen;
size_t len;
len = td->len;
if (td->options & NAND_BBT_VERSION)
len++;
return mtd->read(mtd, offs, len, &retlen, buf);
}
/* /*
* Scan read raw data from flash * Scan read raw data from flash
*/ */
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len) size_t len)
{ {
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
@ -294,6 +364,15 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
return 0; return 0;
} }
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len, struct nand_bbt_descr *td)
{
if (td->options & NAND_BBT_NO_OOB)
return scan_read_raw_data(mtd, buf, offs, td);
else
return scan_read_raw_oob(mtd, buf, offs, len);
}
/* /*
* Scan write data with oob to flash * Scan write data with oob to flash
*/ */
@ -312,6 +391,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
return mtd->write_oob(mtd, offs, &ops); return mtd->write_oob(mtd, offs, &ops);
} }
static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
{
u32 ver_offs = td->veroffs;
if (!(td->options & NAND_BBT_NO_OOB))
ver_offs += mtd->writesize;
return ver_offs;
}
/** /**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
* @mtd: MTD device structure * @mtd: MTD device structure
@ -331,8 +419,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the primary version, if available */ /* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) { if (td->options & NAND_BBT_VERSION) {
scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
mtd->writesize); mtd->writesize, td);
td->version[0] = buf[mtd->writesize + td->veroffs]; td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
td->pages[0], td->version[0]); td->pages[0], td->version[0]);
} }
@ -340,8 +428,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the mirror version, if available */ /* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) { if (md && (md->options & NAND_BBT_VERSION)) {
scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift, scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
mtd->writesize); mtd->writesize, td);
md->version[0] = buf[mtd->writesize + md->veroffs]; md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]); md->pages[0], md->version[0]);
} }
@ -357,7 +445,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
{ {
int ret, j; int ret, j;
ret = scan_read_raw(mtd, buf, offs, readlen); ret = scan_read_raw_oob(mtd, buf, offs, readlen);
if (ret) if (ret)
return ret; return ret;
@ -464,6 +552,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
for (i = startblock; i < numblocks;) { for (i = startblock; i < numblocks;) {
int ret; int ret;
BUG_ON(bd->options & NAND_BBT_NO_OOB);
if (bd->options & NAND_BBT_SCANALLPAGES) if (bd->options & NAND_BBT_SCANALLPAGES)
ret = scan_block_full(mtd, bd, from, buf, readlen, ret = scan_block_full(mtd, bd, from, buf, readlen,
scanlen, len); scanlen, len);
@ -545,11 +635,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t offs = (loff_t)actblock << this->bbt_erase_shift; loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */ /* Read first page */
scan_read_raw(mtd, buf, offs, mtd->writesize); scan_read_raw(mtd, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) { if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage; td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) { if (td->options & NAND_BBT_VERSION) {
td->version[i] = buf[mtd->writesize + td->veroffs]; offs = bbt_get_ver_offs(mtd, td);
td->version[i] = buf[offs];
} }
break; break;
} }
@ -733,12 +824,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
ooboffs = len + (pageoffs * mtd->oobsize); ooboffs = len + (pageoffs * mtd->oobsize);
} else if (td->options & NAND_BBT_NO_OOB) {
ooboffs = 0;
offs = td->len;
/* the version byte */
if (td->options & NAND_BBT_VERSION)
offs++;
/* Calc length */
len = (size_t) (numblocks >> sft);
len += offs;
/* Make it page aligned ! */
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len);
/* Pattern is located at the begin of first page */
memcpy(buf, td->pattern, td->len);
} else { } else {
/* Calc length */ /* Calc length */
len = (size_t) (numblocks >> sft); len = (size_t) (numblocks >> sft);
/* Make it page aligned ! */ /* Make it page aligned ! */
len = (len + (mtd->writesize - 1)) & len = ALIGN(len, mtd->writesize);
~(mtd->writesize - 1);
/* Preset the buffer with 0xff */ /* Preset the buffer with 0xff */
memset(buf, 0xff, len + memset(buf, 0xff, len +
(len >> this->page_shift)* mtd->oobsize); (len >> this->page_shift)* mtd->oobsize);
@ -772,7 +877,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
if (res < 0) if (res < 0)
goto outerr; goto outerr;
res = scan_write_bbt(mtd, to, len, buf, &buf[len]); res = scan_write_bbt(mtd, to, len, buf,
td->options & NAND_BBT_NO_OOB ? NULL :
&buf[len]);
if (res < 0) if (res < 0)
goto outerr; goto outerr;
@ -892,7 +999,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
continue; continue;
/* Create the table in memory by scanning the chip(s) */ /* Create the table in memory by scanning the chip(s) */
create_bbt(mtd, buf, bd, chipsel); if (!(this->options & NAND_CREATE_EMPTY_BBT))
create_bbt(mtd, buf, bd, chipsel);
td->version[i] = 1; td->version[i] = 1;
if (md) if (md)
@ -982,6 +1090,49 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
} }
} }
/**
* verify_bbt_descr - verify the bad block description
* @bd: the table to verify
*
* This functions performs a few sanity checks on the bad block description
* table.
*/
static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
u32 pattern_len = bd->len;
u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
u32 table_size;
if (!bd)
return;
BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
!(this->options & NAND_USE_FLASH_BBT));
BUG_ON(!bits);
if (bd->options & NAND_BBT_VERSION)
pattern_len++;
if (bd->options & NAND_BBT_NO_OOB) {
BUG_ON(!(this->options & NAND_USE_FLASH_BBT));
BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB));
BUG_ON(bd->offs);
if (bd->options & NAND_BBT_VERSION)
BUG_ON(bd->veroffs != bd->len);
BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
}
if (bd->options & NAND_BBT_PERCHIP)
table_size = this->chipsize >> this->bbt_erase_shift;
else
table_size = mtd->size >> this->bbt_erase_shift;
table_size >>= 3;
table_size *= bits;
if (bd->options & NAND_BBT_NO_OOB)
table_size += pattern_len;
BUG_ON(table_size > (1 << this->bbt_erase_shift));
}
/** /**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s) * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
* @mtd: MTD device structure * @mtd: MTD device structure
@ -1023,6 +1174,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
} }
return res; return res;
} }
verify_bbt_descr(mtd, td);
verify_bbt_descr(mtd, md);
/* Allocate a temporary buffer for one eraseblock incl. oob */ /* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift); len = (1 << this->bbt_erase_shift);
@ -1166,6 +1319,26 @@ static struct nand_bbt_descr bbt_mirror_descr = {
.pattern = mirror_pattern .pattern = mirror_pattern
}; };
static struct nand_bbt_descr bbt_main_no_bbt_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB,
.len = 4,
.veroffs = 4,
.maxblocks = 4,
.pattern = bbt_pattern
};
static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB,
.len = 4,
.veroffs = 4,
.maxblocks = 4,
.pattern = mirror_pattern
};
#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \ #define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
NAND_BBT_SCANBYTE1AND6) NAND_BBT_SCANBYTE1AND6)
/** /**
@ -1236,8 +1409,13 @@ int nand_default_bbt(struct mtd_info *mtd)
if (this->options & NAND_USE_FLASH_BBT) { if (this->options & NAND_USE_FLASH_BBT) {
/* Use the default pattern descriptors */ /* Use the default pattern descriptors */
if (!this->bbt_td) { if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr; if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
this->bbt_md = &bbt_mirror_descr; this->bbt_td = &bbt_main_no_bbt_descr;
this->bbt_md = &bbt_mirror_no_bbt_descr;
} else {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
} }
if (!this->badblock_pattern) { if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;

View File

@ -75,9 +75,13 @@ struct nand_flash_dev nand_flash_ids[] = {
/*512 Megabit */ /*512 Megabit */
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16},
/* 1 Gigabit */ /* 1 Gigabit */
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
@ -112,7 +116,34 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
/* 32 Gigabit */ /* 32 Gigabit */
{"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS},
{"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS}, {"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
{"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16},
{"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16},
/* 64 Gigabit */
{"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS},
{"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
{"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16},
{"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16},
/* 128 Gigabit */
{"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS},
{"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
{"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16},
{"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16},
/* 256 Gigabit */
{"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS},
{"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
{"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16},
{"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16},
/* 512 Gigabit */
{"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS},
{"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS},
{"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16},
{"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16},
/* /*
* Renesas AND 1 Gigabit. Those chips do not support extended id and * Renesas AND 1 Gigabit. Those chips do not support extended id and

View File

@ -107,6 +107,7 @@ static char *gravepages = NULL;
static unsigned int rptwear = 0; static unsigned int rptwear = 0;
static unsigned int overridesize = 0; static unsigned int overridesize = 0;
static char *cache_file = NULL; static char *cache_file = NULL;
static unsigned int bbt;
module_param(first_id_byte, uint, 0400); module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400); module_param(second_id_byte, uint, 0400);
@ -130,6 +131,7 @@ module_param(gravepages, charp, 0400);
module_param(rptwear, uint, 0400); module_param(rptwear, uint, 0400);
module_param(overridesize, uint, 0400); module_param(overridesize, uint, 0400);
module_param(cache_file, charp, 0400); module_param(cache_file, charp, 0400);
module_param(bbt, uint, 0400);
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
@ -162,6 +164,7 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I
"The size is specified in erase blocks and as the exponent of a power of two" "The size is specified in erase blocks and as the exponent of a power of two"
" e.g. 5 means a size of 32 erase blocks"); " e.g. 5 means a size of 32 erase blocks");
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory"); MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
/* The largest possible page size */ /* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 4096 #define NS_LARGEST_PAGE_SIZE 4096
@ -2264,6 +2267,18 @@ static int __init ns_init_module(void)
/* and 'badblocks' parameters to work */ /* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN; chip->options |= NAND_SKIP_BBTSCAN;
switch (bbt) {
case 2:
chip->options |= NAND_USE_FLASH_BBT_NO_OOB;
case 1:
chip->options |= NAND_USE_FLASH_BBT;
case 0:
break;
default:
NS_ERR("bbt has to be 0..2\n");
retval = -EINVAL;
goto error;
}
/* /*
* Perform minimum nandsim structure initialization to handle * Perform minimum nandsim structure initialization to handle
* the initial ID read command correctly * the initial ID read command correctly
@ -2321,10 +2336,10 @@ static int __init ns_init_module(void)
if ((retval = init_nandsim(nsmtd)) != 0) if ((retval = init_nandsim(nsmtd)) != 0)
goto err_exit; goto err_exit;
if ((retval = parse_badblocks(nand, nsmtd)) != 0) if ((retval = nand_default_bbt(nsmtd)) != 0)
goto err_exit; goto err_exit;
if ((retval = nand_default_bbt(nsmtd)) != 0) if ((retval = parse_badblocks(nand, nsmtd)) != 0)
goto err_exit; goto err_exit;
/* Register NAND partitions */ /* Register NAND partitions */

View File

@ -229,7 +229,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
const struct of_device_id *match) const struct of_device_id *match)
{ {
struct ndfc_controller *ndfc = &ndfc_ctrl; struct ndfc_controller *ndfc = &ndfc_ctrl;
const u32 *reg; const __be32 *reg;
u32 ccr; u32 ccr;
int err, len; int err, len;
@ -244,7 +244,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
dev_err(&ofdev->dev, "unable read reg property (%d)\n", len); dev_err(&ofdev->dev, "unable read reg property (%d)\n", len);
return -ENOENT; return -ENOENT;
} }
ndfc->chip_select = reg[0]; ndfc->chip_select = be32_to_cpu(reg[0]);
ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0); ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0);
if (!ndfc->ndfcbase) { if (!ndfc->ndfcbase) {
@ -257,7 +257,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
/* It is ok if ccr does not exist - just default to 0 */ /* It is ok if ccr does not exist - just default to 0 */
reg = of_get_property(ofdev->dev.of_node, "ccr", NULL); reg = of_get_property(ofdev->dev.of_node, "ccr", NULL);
if (reg) if (reg)
ccr |= *reg; ccr |= be32_to_cpup(reg);
out_be32(ndfc->ndfcbase + NDFC_CCR, ccr); out_be32(ndfc->ndfcbase + NDFC_CCR, ccr);
@ -265,7 +265,7 @@ static int __devinit ndfc_probe(struct platform_device *ofdev,
reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL); reg = of_get_property(ofdev->dev.of_node, "bank-settings", NULL);
if (reg) { if (reg) {
int offset = NDFC_BCFG0 + (ndfc->chip_select << 2); int offset = NDFC_BCFG0 + (ndfc->chip_select << 2);
out_be32(ndfc->ndfcbase + offset, *reg); out_be32(ndfc->ndfcbase + offset, be32_to_cpup(reg));
} }
err = ndfc_chip_init(ndfc, ofdev->dev.of_node); err = ndfc_chip_init(ndfc, ofdev->dev.of_node);

View File

@ -111,11 +111,11 @@ static int use_dma = 1;
module_param(use_dma, bool, 0); module_param(use_dma, bool, 0);
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
#else #else
const int use_dma; static const int use_dma;
#endif #endif
#else #else
const int use_prefetch; const int use_prefetch;
const int use_dma; static const int use_dma;
#endif #endif
struct omap_nand_info { struct omap_nand_info {

View File

@ -117,7 +117,7 @@ struct pxa3xx_nand_info {
struct nand_chip nand_chip; struct nand_chip nand_chip;
struct platform_device *pdev; struct platform_device *pdev;
const struct pxa3xx_nand_flash *flash_info; struct pxa3xx_nand_cmdset *cmdset;
struct clk *clk; struct clk *clk;
void __iomem *mmio_base; void __iomem *mmio_base;
@ -131,6 +131,7 @@ struct pxa3xx_nand_info {
int drcmr_cmd; int drcmr_cmd;
unsigned char *data_buff; unsigned char *data_buff;
unsigned char *oob_buff;
dma_addr_t data_buff_phys; dma_addr_t data_buff_phys;
size_t data_buff_size; size_t data_buff_size;
int data_dma_ch; int data_dma_ch;
@ -149,7 +150,8 @@ struct pxa3xx_nand_info {
int use_ecc; /* use HW ECC ? */ int use_ecc; /* use HW ECC ? */
int use_dma; /* use DMA ? */ int use_dma; /* use DMA ? */
size_t data_size; /* data size in FIFO */ unsigned int page_size; /* page size of attached chip */
unsigned int data_size; /* data size in FIFO */
int retcode; int retcode;
struct completion cmd_complete; struct completion cmd_complete;
@ -158,6 +160,10 @@ struct pxa3xx_nand_info {
uint32_t ndcb1; uint32_t ndcb1;
uint32_t ndcb2; uint32_t ndcb2;
/* timing calcuted from setting */
uint32_t ndtr0cs0;
uint32_t ndtr1cs0;
/* calculated from pxa3xx_nand_flash data */ /* calculated from pxa3xx_nand_flash data */
size_t oob_size; size_t oob_size;
size_t read_id_bytes; size_t read_id_bytes;
@ -174,23 +180,7 @@ MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
* Default NAND flash controller configuration setup by the * Default NAND flash controller configuration setup by the
* bootloader. This configuration is used only when pdata->keep_config is set * bootloader. This configuration is used only when pdata->keep_config is set
*/ */
static struct pxa3xx_nand_timing default_timing; static struct pxa3xx_nand_cmdset default_cmdset = {
static struct pxa3xx_nand_flash default_flash;
static struct pxa3xx_nand_cmdset smallpage_cmdset = {
.read1 = 0x0000,
.read2 = 0x0050,
.program = 0x1080,
.read_status = 0x0070,
.read_id = 0x0090,
.erase = 0xD060,
.reset = 0x00FF,
.lock = 0x002A,
.unlock = 0x2423,
.lock_status = 0x007A,
};
static struct pxa3xx_nand_cmdset largepage_cmdset = {
.read1 = 0x3000, .read1 = 0x3000,
.read2 = 0x0050, .read2 = 0x0050,
.program = 0x1080, .program = 0x1080,
@ -203,142 +193,27 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = {
.lock_status = 0x007A, .lock_status = 0x007A,
}; };
#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN static struct pxa3xx_nand_timing timing[] = {
static struct pxa3xx_nand_timing samsung512MbX16_timing = { { 40, 80, 60, 100, 80, 100, 90000, 400, 40, },
.tCH = 10, { 10, 0, 20, 40, 30, 40, 11123, 110, 10, },
.tCS = 0, { 10, 25, 15, 25, 15, 30, 25000, 60, 10, },
.tWH = 20, { 10, 35, 15, 25, 15, 25, 25000, 60, 10, },
.tWP = 40,
.tRH = 30,
.tRP = 40,
.tR = 11123,
.tWHR = 110,
.tAR = 10,
}; };
static struct pxa3xx_nand_flash samsung512MbX16 = { static struct pxa3xx_nand_flash builtin_flash_types[] = {
.timing = &samsung512MbX16_timing, { 0, 0, 2048, 8, 8, 0, &default_cmdset, &timing[0] },
.cmdset = &smallpage_cmdset, { 0x46ec, 32, 512, 16, 16, 4096, &default_cmdset, &timing[1] },
.page_per_block = 32, { 0xdaec, 64, 2048, 8, 8, 2048, &default_cmdset, &timing[1] },
.page_size = 512, { 0xd7ec, 128, 4096, 8, 8, 8192, &default_cmdset, &timing[1] },
.flash_width = 16, { 0xa12c, 64, 2048, 8, 8, 1024, &default_cmdset, &timing[2] },
.dfc_width = 16, { 0xb12c, 64, 2048, 16, 16, 1024, &default_cmdset, &timing[2] },
.num_blocks = 4096, { 0xdc2c, 64, 2048, 8, 8, 4096, &default_cmdset, &timing[2] },
.chip_id = 0x46ec, { 0xcc2c, 64, 2048, 16, 16, 4096, &default_cmdset, &timing[2] },
{ 0xba20, 64, 2048, 16, 16, 2048, &default_cmdset, &timing[3] },
}; };
static struct pxa3xx_nand_flash samsung2GbX8 = { /* Define a default flash type setting serve as flash detecting only */
.timing = &samsung512MbX16_timing, #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0])
.cmdset = &smallpage_cmdset,
.page_per_block = 64,
.page_size = 2048,
.flash_width = 8,
.dfc_width = 8,
.num_blocks = 2048,
.chip_id = 0xdaec,
};
static struct pxa3xx_nand_flash samsung32GbX8 = {
.timing = &samsung512MbX16_timing,
.cmdset = &smallpage_cmdset,
.page_per_block = 128,
.page_size = 4096,
.flash_width = 8,
.dfc_width = 8,
.num_blocks = 8192,
.chip_id = 0xd7ec,
};
static struct pxa3xx_nand_timing micron_timing = {
.tCH = 10,
.tCS = 25,
.tWH = 15,
.tWP = 25,
.tRH = 15,
.tRP = 30,
.tR = 25000,
.tWHR = 60,
.tAR = 10,
};
static struct pxa3xx_nand_flash micron1GbX8 = {
.timing = &micron_timing,
.cmdset = &largepage_cmdset,
.page_per_block = 64,
.page_size = 2048,
.flash_width = 8,
.dfc_width = 8,
.num_blocks = 1024,
.chip_id = 0xa12c,
};
static struct pxa3xx_nand_flash micron1GbX16 = {
.timing = &micron_timing,
.cmdset = &largepage_cmdset,
.page_per_block = 64,
.page_size = 2048,
.flash_width = 16,
.dfc_width = 16,
.num_blocks = 1024,
.chip_id = 0xb12c,
};
static struct pxa3xx_nand_flash micron4GbX8 = {
.timing = &micron_timing,
.cmdset = &largepage_cmdset,
.page_per_block = 64,
.page_size = 2048,
.flash_width = 8,
.dfc_width = 8,
.num_blocks = 4096,
.chip_id = 0xdc2c,
};
static struct pxa3xx_nand_flash micron4GbX16 = {
.timing = &micron_timing,
.cmdset = &largepage_cmdset,
.page_per_block = 64,
.page_size = 2048,
.flash_width = 16,
.dfc_width = 16,
.num_blocks = 4096,
.chip_id = 0xcc2c,
};
static struct pxa3xx_nand_timing stm2GbX16_timing = {
.tCH = 10,
.tCS = 35,
.tWH = 15,
.tWP = 25,
.tRH = 15,
.tRP = 25,
.tR = 25000,
.tWHR = 60,
.tAR = 10,
};
static struct pxa3xx_nand_flash stm2GbX16 = {
.timing = &stm2GbX16_timing,
.cmdset = &largepage_cmdset,
.page_per_block = 64,
.page_size = 2048,
.flash_width = 16,
.dfc_width = 16,
.num_blocks = 2048,
.chip_id = 0xba20,
};
static struct pxa3xx_nand_flash *builtin_flash_types[] = {
&samsung512MbX16,
&samsung2GbX8,
&samsung32GbX8,
&micron1GbX8,
&micron1GbX16,
&micron4GbX8,
&micron4GbX16,
&stm2GbX16,
};
#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */
#define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCH(c) (min((c), 7) << 19)
#define NDTR0_tCS(c) (min((c), 7) << 16) #define NDTR0_tCS(c) (min((c), 7) << 16)
@ -351,23 +226,9 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
#define NDTR1_tWHR(c) (min((c), 15) << 4) #define NDTR1_tWHR(c) (min((c), 15) << 4)
#define NDTR1_tAR(c) (min((c), 15) << 0) #define NDTR1_tAR(c) (min((c), 15) << 0)
#define tCH_NDTR0(r) (((r) >> 19) & 0x7)
#define tCS_NDTR0(r) (((r) >> 16) & 0x7)
#define tWH_NDTR0(r) (((r) >> 11) & 0x7)
#define tWP_NDTR0(r) (((r) >> 8) & 0x7)
#define tRH_NDTR0(r) (((r) >> 3) & 0x7)
#define tRP_NDTR0(r) (((r) >> 0) & 0x7)
#define tR_NDTR1(r) (((r) >> 16) & 0xffff)
#define tWHR_NDTR1(r) (((r) >> 4) & 0xf)
#define tAR_NDTR1(r) (((r) >> 0) & 0xf)
/* convert nano-seconds to nand flash controller clock cycles */ /* convert nano-seconds to nand flash controller clock cycles */
#define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000) #define ns2cycle(ns, clk) (int)((ns) * (clk / 1000000) / 1000)
/* convert nand flash controller clock cycles to nano-seconds */
#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_timing *t) const struct pxa3xx_nand_timing *t)
{ {
@ -385,6 +246,8 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |
NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); NDTR1_tAR(ns2cycle(t->tAR, nand_clk));
info->ndtr0cs0 = ndtr0;
info->ndtr1cs0 = ndtr1;
nand_writel(info, NDTR0CS0, ndtr0); nand_writel(info, NDTR0CS0, ndtr0);
nand_writel(info, NDTR1CS0, ndtr1); nand_writel(info, NDTR1CS0, ndtr1);
} }
@ -408,23 +271,31 @@ static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event)
return -ETIMEDOUT; return -ETIMEDOUT;
} }
static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)
uint16_t cmd, int column, int page_addr)
{ {
const struct pxa3xx_nand_flash *f = info->flash_info; int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
/* calculate data size */ info->data_size = info->page_size;
switch (f->page_size) { if (!oob_enable) {
info->oob_size = 0;
return;
}
switch (info->page_size) {
case 2048: case 2048:
info->data_size = (info->use_ecc) ? 2088 : 2112; info->oob_size = (info->use_ecc) ? 40 : 64;
break; break;
case 512: case 512:
info->data_size = (info->use_ecc) ? 520 : 528; info->oob_size = (info->use_ecc) ? 8 : 16;
break; break;
default:
return -EINVAL;
} }
}
static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
uint16_t cmd, int column, int page_addr)
{
const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
pxa3xx_set_datasize(info);
/* generate values for NDCBx registers */ /* generate values for NDCBx registers */
info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
@ -463,12 +334,13 @@ static int prepare_erase_cmd(struct pxa3xx_nand_info *info,
static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd)
{ {
const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
info->ndcb1 = 0; info->ndcb1 = 0;
info->ndcb2 = 0; info->ndcb2 = 0;
info->oob_size = 0;
if (cmd == cmdset->read_id) { if (cmd == cmdset->read_id) {
info->ndcb0 |= NDCB0_CMD_TYPE(3); info->ndcb0 |= NDCB0_CMD_TYPE(3);
info->data_size = 8; info->data_size = 8;
@ -537,6 +409,9 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
case STATE_PIO_WRITING: case STATE_PIO_WRITING:
__raw_writesl(info->mmio_base + NDDB, info->data_buff, __raw_writesl(info->mmio_base + NDDB, info->data_buff,
DIV_ROUND_UP(info->data_size, 4)); DIV_ROUND_UP(info->data_size, 4));
if (info->oob_size > 0)
__raw_writesl(info->mmio_base + NDDB, info->oob_buff,
DIV_ROUND_UP(info->oob_size, 4));
enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD);
@ -549,6 +424,9 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
case STATE_PIO_READING: case STATE_PIO_READING:
__raw_readsl(info->mmio_base + NDDB, info->data_buff, __raw_readsl(info->mmio_base + NDDB, info->data_buff,
DIV_ROUND_UP(info->data_size, 4)); DIV_ROUND_UP(info->data_size, 4));
if (info->oob_size > 0)
__raw_readsl(info->mmio_base + NDDB, info->oob_buff,
DIV_ROUND_UP(info->oob_size, 4));
break; break;
default: default:
printk(KERN_ERR "%s: invalid state %d\n", __func__, printk(KERN_ERR "%s: invalid state %d\n", __func__,
@ -563,7 +441,7 @@ static int handle_data_pio(struct pxa3xx_nand_info *info)
static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out)
{ {
struct pxa_dma_desc *desc = info->data_desc; struct pxa_dma_desc *desc = info->data_desc;
int dma_len = ALIGN(info->data_size, 32); int dma_len = ALIGN(info->data_size + info->oob_size, 32);
desc->ddadr = DDADR_STOP; desc->ddadr = DDADR_STOP;
desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len;
@ -700,8 +578,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
int column, int page_addr) int column, int page_addr)
{ {
struct pxa3xx_nand_info *info = mtd->priv; struct pxa3xx_nand_info *info = mtd->priv;
const struct pxa3xx_nand_flash *flash_info = info->flash_info; const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
const struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset;
int ret; int ret;
info->use_dma = (use_dma) ? 1 : 0; info->use_dma = (use_dma) ? 1 : 0;
@ -925,8 +802,7 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) static int __readid(struct pxa3xx_nand_info *info, uint32_t *id)
{ {
const struct pxa3xx_nand_flash *f = info->flash_info; const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
const struct pxa3xx_nand_cmdset *cmdset = f->cmdset;
uint32_t ndcr; uint32_t ndcr;
uint8_t id_buff[8]; uint8_t id_buff[8];
@ -968,7 +844,9 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
return -EINVAL; return -EINVAL;
/* calculate flash information */ /* calculate flash information */
info->oob_size = (f->page_size == 2048) ? 64 : 16; info->cmdset = f->cmdset;
info->page_size = f->page_size;
info->oob_buff = info->data_buff + f->page_size;
info->read_id_bytes = (f->page_size == 2048) ? 4 : 2; info->read_id_bytes = (f->page_size == 2048) ? 4 : 2;
/* calculate addressing information */ /* calculate addressing information */
@ -992,49 +870,20 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
info->reg_ndcr = ndcr; info->reg_ndcr = ndcr;
pxa3xx_nand_set_timing(info, f->timing); pxa3xx_nand_set_timing(info, f->timing);
info->flash_info = f;
return 0; return 0;
} }
static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
struct pxa3xx_nand_timing *t)
{
unsigned long nand_clk = clk_get_rate(info->clk);
uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
}
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
{ {
uint32_t ndcr = nand_readl(info, NDCR); uint32_t ndcr = nand_readl(info, NDCR);
struct nand_flash_dev *type = NULL; struct nand_flash_dev *type = NULL;
uint32_t id = -1; uint32_t id = -1, page_per_block, num_blocks;
int i; int i;
default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32; page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
if (default_flash.page_size == 2048)
default_flash.cmdset = &largepage_cmdset;
else
default_flash.cmdset = &smallpage_cmdset;
/* set info fields needed to __readid */ /* set info fields needed to __readid */
info->flash_info = &default_flash; info->read_id_bytes = (info->page_size == 2048) ? 4 : 2;
info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
info->reg_ndcr = ndcr; info->reg_ndcr = ndcr;
if (__readid(info, &id)) if (__readid(info, &id))
@ -1053,21 +902,20 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
return -ENODEV; return -ENODEV;
/* fill the missing flash information */ /* fill the missing flash information */
i = __ffs(default_flash.page_per_block * default_flash.page_size); i = __ffs(page_per_block * info->page_size);
default_flash.num_blocks = type->chipsize << (20 - i); num_blocks = type->chipsize << (20 - i);
info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
/* calculate addressing information */ /* calculate addressing information */
info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1; info->col_addr_cycles = (info->page_size == 2048) ? 2 : 1;
if (default_flash.num_blocks * default_flash.page_per_block > 65536) if (num_blocks * page_per_block > 65536)
info->row_addr_cycles = 3; info->row_addr_cycles = 3;
else else
info->row_addr_cycles = 2; info->row_addr_cycles = 2;
pxa3xx_nand_detect_timing(info, &default_timing); info->ndtr0cs0 = nand_readl(info, NDTR0CS0);
default_flash.timing = &default_timing; info->ndtr1cs0 = nand_readl(info, NDTR1CS0);
info->cmdset = &default_cmdset;
return 0; return 0;
} }
@ -1083,38 +931,29 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
if (pxa3xx_nand_detect_config(info) == 0) if (pxa3xx_nand_detect_config(info) == 0)
return 0; return 0;
for (i = 0; i<pdata->num_flash; ++i) { /* we use default timing to detect id */
f = pdata->flash + i; f = DEFAULT_FLASH_TYPE;
pxa3xx_nand_config_flash(info, f);
if (__readid(info, &id))
goto fail_detect;
if (pxa3xx_nand_config_flash(info, f)) for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) {
continue; /* we first choose the flash definition from platfrom */
if (i < pdata->num_flash)
if (__readid(info, &id)) f = pdata->flash + i;
continue; else
f = &builtin_flash_types[i - pdata->num_flash + 1];
if (id == f->chip_id) if (f->chip_id == id) {
dev_info(&info->pdev->dev, "detect chip id: 0x%x\n", id);
pxa3xx_nand_config_flash(info, f);
return 0; return 0;
}
} }
#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) {
f = builtin_flash_types[i];
if (pxa3xx_nand_config_flash(info, f))
continue;
if (__readid(info, &id))
continue;
if (id == f->chip_id)
return 0;
}
#endif
dev_warn(&info->pdev->dev, dev_warn(&info->pdev->dev,
"failed to detect configured nand flash; found %04x instead of\n", "failed to detect configured nand flash; found %04x instead of\n",
id); id);
fail_detect:
return -ENODEV; return -ENODEV;
} }
@ -1177,10 +1016,9 @@ static struct nand_ecclayout hw_largepage_ecclayout = {
static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
struct pxa3xx_nand_info *info) struct pxa3xx_nand_info *info)
{ {
const struct pxa3xx_nand_flash *f = info->flash_info;
struct nand_chip *this = &info->nand_chip; struct nand_chip *this = &info->nand_chip;
this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0; this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0;
this->waitfunc = pxa3xx_nand_waitfunc; this->waitfunc = pxa3xx_nand_waitfunc;
this->select_chip = pxa3xx_nand_select_chip; this->select_chip = pxa3xx_nand_select_chip;
@ -1196,9 +1034,9 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
this->ecc.hwctl = pxa3xx_nand_ecc_hwctl; this->ecc.hwctl = pxa3xx_nand_ecc_hwctl;
this->ecc.calculate = pxa3xx_nand_ecc_calculate; this->ecc.calculate = pxa3xx_nand_ecc_calculate;
this->ecc.correct = pxa3xx_nand_ecc_correct; this->ecc.correct = pxa3xx_nand_ecc_correct;
this->ecc.size = f->page_size; this->ecc.size = info->page_size;
if (f->page_size == 2048) if (info->page_size == 2048)
this->ecc.layout = &hw_largepage_ecclayout; this->ecc.layout = &hw_largepage_ecclayout;
else else
this->ecc.layout = &hw_smallpage_ecclayout; this->ecc.layout = &hw_smallpage_ecclayout;
@ -1411,9 +1249,11 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev); struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(pdev);
struct pxa3xx_nand_info *info = mtd->priv; struct pxa3xx_nand_info *info = mtd->priv;
nand_writel(info, NDTR0CS0, info->ndtr0cs0);
nand_writel(info, NDTR1CS0, info->ndtr1cs0);
clk_enable(info->clk); clk_enable(info->clk);
return pxa3xx_nand_config_flash(info, info->flash_info); return 0;
} }
#else #else
#define pxa3xx_nand_suspend NULL #define pxa3xx_nand_suspend NULL

View File

@ -757,11 +757,6 @@ static irqreturn_t r852_irq(int irq, void *data)
spin_lock_irqsave(&dev->irqlock, flags); spin_lock_irqsave(&dev->irqlock, flags);
/* We can recieve shared interrupt while pci is suspended
in that case reads will return 0xFFFFFFFF.... */
if (dev->insuspend)
goto out;
/* handle card detection interrupts first */ /* handle card detection interrupts first */
card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
@ -1035,7 +1030,6 @@ void r852_shutdown(struct pci_dev *pci_dev)
int r852_suspend(struct device *device) int r852_suspend(struct device *device)
{ {
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
unsigned long flags;
if (dev->ctlreg & R852_CTL_CARDENABLE) if (dev->ctlreg & R852_CTL_CARDENABLE)
return -EBUSY; return -EBUSY;
@ -1047,43 +1041,22 @@ int r852_suspend(struct device *device)
r852_disable_irqs(dev); r852_disable_irqs(dev);
r852_engine_disable(dev); r852_engine_disable(dev);
spin_lock_irqsave(&dev->irqlock, flags);
dev->insuspend = 1;
spin_unlock_irqrestore(&dev->irqlock, flags);
/* At that point, even if interrupt handler is running, it will quit */
/* So wait for this to happen explictly */
synchronize_irq(dev->irq);
/* If card was pulled off just during the suspend, which is very /* If card was pulled off just during the suspend, which is very
unlikely, we will remove it on resume, it too late now unlikely, we will remove it on resume, it too late now
anyway... */ anyway... */
dev->card_unstable = 0; dev->card_unstable = 0;
return 0;
pci_save_state(to_pci_dev(device));
return pci_prepare_to_sleep(to_pci_dev(device));
} }
int r852_resume(struct device *device) int r852_resume(struct device *device)
{ {
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
unsigned long flags;
/* Turn on the hardware */
pci_back_from_sleep(to_pci_dev(device));
pci_restore_state(to_pci_dev(device));
r852_disable_irqs(dev); r852_disable_irqs(dev);
r852_card_update_present(dev); r852_card_update_present(dev);
r852_engine_disable(dev); r852_engine_disable(dev);
/* Now its safe for IRQ to run */
spin_lock_irqsave(&dev->irqlock, flags);
dev->insuspend = 0;
spin_unlock_irqrestore(&dev->irqlock, flags);
/* If card status changed, just do the work */ /* If card status changed, just do the work */
if (dev->card_detected != dev->card_registred) { if (dev->card_detected != dev->card_registred) {
dbg("card was %s during low power state", dbg("card was %s during low power state",
@ -1121,7 +1094,6 @@ MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
static struct pci_driver r852_pci_driver = { static struct pci_driver r852_pci_driver = {
.name = DRV_NAME, .name = DRV_NAME,
.id_table = r852_pci_id_tbl, .id_table = r852_pci_id_tbl,

View File

@ -140,8 +140,6 @@ struct r852_device {
/* interrupt handling */ /* interrupt handling */
spinlock_t irqlock; /* IRQ protecting lock */ spinlock_t irqlock; /* IRQ protecting lock */
int irq; /* irq num */ int irq; /* irq num */
int insuspend; /* device is suspended */
/* misc */ /* misc */
void *tmp_buffer; /* temporary buffer */ void *tmp_buffer; /* temporary buffer */
uint8_t ctlreg; /* cached contents of control reg */ uint8_t ctlreg; /* cached contents of control reg */

View File

@ -44,7 +44,7 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
pp = NULL; pp = NULL;
i = 0; i = 0;
while ((pp = of_get_next_child(node, pp))) { while ((pp = of_get_next_child(node, pp))) {
const u32 *reg; const __be32 *reg;
int len; int len;
reg = of_get_property(pp, "reg", &len); reg = of_get_property(pp, "reg", &len);

View File

@ -32,10 +32,11 @@ config MTD_ONENAND_OMAP2
config MTD_ONENAND_SAMSUNG config MTD_ONENAND_SAMSUNG
tristate "OneNAND on Samsung SOC controller support" tristate "OneNAND on Samsung SOC controller support"
depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 depends on ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210 || ARCH_S5PV310
help help
Support for a OneNAND flash device connected to an Samsung SOC Support for a OneNAND flash device connected to an Samsung SOC.
S3C64XX/S5PC1XX controller. S3C64XX/S5PC100 use command mapping method.
S5PC110/S5PC210 use generic OneNAND method.
config MTD_ONENAND_OTP config MTD_ONENAND_OTP
bool "OneNAND OTP Support" bool "OneNAND OTP Support"

View File

@ -3365,18 +3365,19 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
static void onenand_check_features(struct mtd_info *mtd) static void onenand_check_features(struct mtd_info *mtd)
{ {
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
unsigned int density, process; unsigned int density, process, numbufs;
/* Lock scheme depends on density and process */ /* Lock scheme depends on density and process */
density = onenand_get_density(this->device_id); density = onenand_get_density(this->device_id);
process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8;
/* Lock scheme */ /* Lock scheme */
switch (density) { switch (density) {
case ONENAND_DEVICE_DENSITY_4Gb: case ONENAND_DEVICE_DENSITY_4Gb:
if (ONENAND_IS_DDP(this)) if (ONENAND_IS_DDP(this))
this->options |= ONENAND_HAS_2PLANE; this->options |= ONENAND_HAS_2PLANE;
else else if (numbufs == 1)
this->options |= ONENAND_HAS_4KB_PAGE; this->options |= ONENAND_HAS_4KB_PAGE;
case ONENAND_DEVICE_DENSITY_2Gb: case ONENAND_DEVICE_DENSITY_2Gb:
@ -4027,7 +4028,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->ecclayout = this->ecclayout; mtd->ecclayout = this->ecclayout;
/* Fill in remaining MTD driver data */ /* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH; mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH;
mtd->erase = onenand_erase; mtd->erase = onenand_erase;
mtd->point = NULL; mtd->point = NULL;

View File

@ -22,6 +22,7 @@
#include <linux/mtd/onenand.h> #include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <asm/mach/flash.h> #include <asm/mach/flash.h>
#include <plat/regs-onenand.h> #include <plat/regs-onenand.h>
@ -58,7 +59,7 @@ enum soc_type {
#define MAP_11 (0x3) #define MAP_11 (0x3)
#define S3C64XX_CMD_MAP_SHIFT 24 #define S3C64XX_CMD_MAP_SHIFT 24
#define S5PC1XX_CMD_MAP_SHIFT 26 #define S5PC100_CMD_MAP_SHIFT 26
#define S3C6400_FBA_SHIFT 10 #define S3C6400_FBA_SHIFT 10
#define S3C6400_FPA_SHIFT 4 #define S3C6400_FPA_SHIFT 4
@ -81,6 +82,17 @@ enum soc_type {
#define S5PC110_DMA_TRANS_CMD 0x418 #define S5PC110_DMA_TRANS_CMD 0x418
#define S5PC110_DMA_TRANS_STATUS 0x41C #define S5PC110_DMA_TRANS_STATUS 0x41C
#define S5PC110_DMA_TRANS_DIR 0x420 #define S5PC110_DMA_TRANS_DIR 0x420
#define S5PC110_INTC_DMA_CLR 0x1004
#define S5PC110_INTC_ONENAND_CLR 0x1008
#define S5PC110_INTC_DMA_MASK 0x1024
#define S5PC110_INTC_ONENAND_MASK 0x1028
#define S5PC110_INTC_DMA_PEND 0x1044
#define S5PC110_INTC_ONENAND_PEND 0x1048
#define S5PC110_INTC_DMA_STATUS 0x1064
#define S5PC110_INTC_ONENAND_STATUS 0x1068
#define S5PC110_INTC_DMA_TD (1 << 24)
#define S5PC110_INTC_DMA_TE (1 << 16)
#define S5PC110_DMA_CFG_SINGLE (0x0 << 16) #define S5PC110_DMA_CFG_SINGLE (0x0 << 16)
#define S5PC110_DMA_CFG_4BURST (0x2 << 16) #define S5PC110_DMA_CFG_4BURST (0x2 << 16)
@ -134,6 +146,7 @@ struct s3c_onenand {
void __iomem *dma_addr; void __iomem *dma_addr;
struct resource *dma_res; struct resource *dma_res;
unsigned long phys_base; unsigned long phys_base;
struct completion complete;
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *parts; struct mtd_partition *parts;
#endif #endif
@ -191,7 +204,7 @@ static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val)
static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val) static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val)
{ {
return (type << S5PC1XX_CMD_MAP_SHIFT) | val; return (type << S5PC100_CMD_MAP_SHIFT) | val;
} }
static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa)
@ -531,10 +544,13 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,
return 0; return 0;
} }
static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) static int (*s5pc110_dma_ops)(void *dst, void *src, size_t count, int direction);
static int s5pc110_dma_poll(void *dst, void *src, size_t count, int direction)
{ {
void __iomem *base = onenand->dma_addr; void __iomem *base = onenand->dma_addr;
int status; int status;
unsigned long timeout;
writel(src, base + S5PC110_DMA_SRC_ADDR); writel(src, base + S5PC110_DMA_SRC_ADDR);
writel(dst, base + S5PC110_DMA_DST_ADDR); writel(dst, base + S5PC110_DMA_DST_ADDR);
@ -552,6 +568,13 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
/*
* There's no exact timeout values at Spec.
* In real case it takes under 1 msec.
* So 20 msecs are enough.
*/
timeout = jiffies + msecs_to_jiffies(20);
do { do {
status = readl(base + S5PC110_DMA_TRANS_STATUS); status = readl(base + S5PC110_DMA_TRANS_STATUS);
if (status & S5PC110_DMA_TRANS_STATUS_TE) { if (status & S5PC110_DMA_TRANS_STATUS_TE) {
@ -559,13 +582,68 @@ static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction)
base + S5PC110_DMA_TRANS_CMD); base + S5PC110_DMA_TRANS_CMD);
return -EIO; return -EIO;
} }
} while (!(status & S5PC110_DMA_TRANS_STATUS_TD)); } while (!(status & S5PC110_DMA_TRANS_STATUS_TD) &&
time_before(jiffies, timeout));
writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD);
return 0; return 0;
} }
static irqreturn_t s5pc110_onenand_irq(int irq, void *data)
{
void __iomem *base = onenand->dma_addr;
int status, cmd = 0;
status = readl(base + S5PC110_INTC_DMA_STATUS);
if (likely(status & S5PC110_INTC_DMA_TD))
cmd = S5PC110_DMA_TRANS_CMD_TDC;
if (unlikely(status & S5PC110_INTC_DMA_TE))
cmd = S5PC110_DMA_TRANS_CMD_TEC;
writel(cmd, base + S5PC110_DMA_TRANS_CMD);
writel(status, base + S5PC110_INTC_DMA_CLR);
if (!onenand->complete.done)
complete(&onenand->complete);
return IRQ_HANDLED;
}
static int s5pc110_dma_irq(void *dst, void *src, size_t count, int direction)
{
void __iomem *base = onenand->dma_addr;
int status;
status = readl(base + S5PC110_INTC_DMA_MASK);
if (status) {
status &= ~(S5PC110_INTC_DMA_TD | S5PC110_INTC_DMA_TE);
writel(status, base + S5PC110_INTC_DMA_MASK);
}
writel(src, base + S5PC110_DMA_SRC_ADDR);
writel(dst, base + S5PC110_DMA_DST_ADDR);
if (direction == S5PC110_DMA_DIR_READ) {
writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG);
} else {
writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG);
writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG);
}
writel(count, base + S5PC110_DMA_TRANS_SIZE);
writel(direction, base + S5PC110_DMA_TRANS_DIR);
writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD);
wait_for_completion_timeout(&onenand->complete, msecs_to_jiffies(20));
return 0;
}
static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
unsigned char *buffer, int offset, size_t count) unsigned char *buffer, int offset, size_t count)
{ {
@ -573,7 +651,8 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
void __iomem *p; void __iomem *p;
void *buf = (void *) buffer; void *buf = (void *) buffer;
dma_addr_t dma_src, dma_dst; dma_addr_t dma_src, dma_dst;
int err; int err, page_dma = 0;
struct device *dev = &onenand->pdev->dev;
p = this->base + area; p = this->base + area;
if (ONENAND_CURRENT_BUFFERRAM(this)) { if (ONENAND_CURRENT_BUFFERRAM(this)) {
@ -597,21 +676,27 @@ static int s5pc110_read_bufferram(struct mtd_info *mtd, int area,
page = vmalloc_to_page(buf); page = vmalloc_to_page(buf);
if (!page) if (!page)
goto normal; goto normal;
buf = page_address(page) + ((size_t) buf & ~PAGE_MASK);
}
/* DMA routine */ page_dma = 1;
dma_src = onenand->phys_base + (p - this->base); /* DMA routine */
dma_dst = dma_map_single(&onenand->pdev->dev, dma_src = onenand->phys_base + (p - this->base);
buf, count, DMA_FROM_DEVICE); dma_dst = dma_map_page(dev, page, 0, count, DMA_FROM_DEVICE);
if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) { } else {
dev_err(&onenand->pdev->dev, /* DMA routine */
"Couldn't map a %d byte buffer for DMA\n", count); dma_src = onenand->phys_base + (p - this->base);
dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE);
}
if (dma_mapping_error(dev, dma_dst)) {
dev_err(dev, "Couldn't map a %d byte buffer for DMA\n", count);
goto normal; goto normal;
} }
err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src, err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src,
count, S5PC110_DMA_DIR_READ); count, S5PC110_DMA_DIR_READ);
dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE);
if (page_dma)
dma_unmap_page(dev, dma_dst, count, DMA_FROM_DEVICE);
else
dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE);
if (!err) if (!err)
return 0; return 0;
@ -759,7 +844,6 @@ static void s3c_onenand_setup(struct mtd_info *mtd)
onenand->cmd_map = s5pc1xx_cmd_map; onenand->cmd_map = s5pc1xx_cmd_map;
} else if (onenand->type == TYPE_S5PC110) { } else if (onenand->type == TYPE_S5PC110) {
/* Use generic onenand functions */ /* Use generic onenand functions */
onenand->cmd_map = s5pc1xx_cmd_map;
this->read_bufferram = s5pc110_read_bufferram; this->read_bufferram = s5pc110_read_bufferram;
this->chip_probe = s5pc110_chip_probe; this->chip_probe = s5pc110_chip_probe;
return; return;
@ -904,6 +988,20 @@ static int s3c_onenand_probe(struct platform_device *pdev)
} }
onenand->phys_base = onenand->base_res->start; onenand->phys_base = onenand->base_res->start;
s5pc110_dma_ops = s5pc110_dma_poll;
/* Interrupt support */
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (r) {
init_completion(&onenand->complete);
s5pc110_dma_ops = s5pc110_dma_irq;
err = request_irq(r->start, s5pc110_onenand_irq,
IRQF_SHARED, "onenand", &onenand);
if (err) {
dev_err(&pdev->dev, "failed to get irq\n");
goto scan_failed;
}
}
} }
if (onenand_scan(mtd, 1)) { if (onenand_scan(mtd, 1)) {
@ -1000,7 +1098,7 @@ static int s3c_pm_ops_suspend(struct device *dev)
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
this->wait(mtd, FL_PM_SUSPENDED); this->wait(mtd, FL_PM_SUSPENDED);
return mtd->suspend(mtd); return 0;
} }
static int s3c_pm_ops_resume(struct device *dev) static int s3c_pm_ops_resume(struct device *dev)
@ -1009,7 +1107,6 @@ static int s3c_pm_ops_resume(struct device *dev)
struct mtd_info *mtd = platform_get_drvdata(pdev); struct mtd_info *mtd = platform_get_drvdata(pdev);
struct onenand_chip *this = mtd->priv; struct onenand_chip *this = mtd->priv;
mtd->resume(mtd);
this->unlock_all(mtd); this->unlock_all(mtd);
return 0; return 0;
} }

View File

@ -20,7 +20,7 @@
struct ftl_zone { struct ftl_zone {
int initialized; bool initialized;
int16_t *lba_to_phys_table; /* LBA to physical table */ int16_t *lba_to_phys_table; /* LBA to physical table */
struct kfifo free_sectors; /* queue of free sectors */ struct kfifo free_sectors; /* queue of free sectors */
}; };
@ -37,8 +37,8 @@ struct sm_ftl {
int zone_count; /* number of zones */ int zone_count; /* number of zones */
int max_lba; /* maximum lba in a zone */ int max_lba; /* maximum lba in a zone */
int smallpagenand; /* 256 bytes/page nand */ int smallpagenand; /* 256 bytes/page nand */
int readonly; /* is FS readonly */ bool readonly; /* is FS readonly */
int unstable; bool unstable;
int cis_block; /* CIS block location */ int cis_block; /* CIS block location */
int cis_boffset; /* CIS offset in the block */ int cis_boffset; /* CIS offset in the block */
int cis_page_offset; /* CIS offset in the page */ int cis_page_offset; /* CIS offset in the page */
@ -49,7 +49,7 @@ struct sm_ftl {
int cache_zone; /* zone of cached block */ int cache_zone; /* zone of cached block */
unsigned char *cache_data; /* cached block data */ unsigned char *cache_data; /* cached block data */
long unsigned int cache_data_invalid_bitmap; long unsigned int cache_data_invalid_bitmap;
int cache_clean; bool cache_clean;
struct work_struct flush_work; struct work_struct flush_work;
struct timer_list timer; struct timer_list timer;

View File

@ -23,7 +23,7 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *,
static inline struct jffs2_inode_cache * static inline struct jffs2_inode_cache *
first_inode_chain(int *i, struct jffs2_sb_info *c) first_inode_chain(int *i, struct jffs2_sb_info *c)
{ {
for (; *i < INOCACHE_HASHSIZE; (*i)++) { for (; *i < c->inocache_hashsize; (*i)++) {
if (c->inocache_list[*i]) if (c->inocache_list[*i])
return c->inocache_list[*i]; return c->inocache_list[*i];
} }

View File

@ -103,7 +103,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
spin_unlock(&jffs2_compressor_list_lock); spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen; *datalen = orig_slen;
*cdatalen = orig_dlen; *cdatalen = orig_dlen;
compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); compr_ret = this->compress(data_in, output_buf, datalen, cdatalen);
spin_lock(&jffs2_compressor_list_lock); spin_lock(&jffs2_compressor_list_lock);
this->usecount--; this->usecount--;
if (!compr_ret) { if (!compr_ret) {
@ -152,7 +152,7 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
spin_unlock(&jffs2_compressor_list_lock); spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen; *datalen = orig_slen;
*cdatalen = orig_dlen; *cdatalen = orig_dlen;
compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen);
spin_lock(&jffs2_compressor_list_lock); spin_lock(&jffs2_compressor_list_lock);
this->usecount--; this->usecount--;
if (!compr_ret) { if (!compr_ret) {
@ -220,7 +220,7 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
if (comprtype == this->compr) { if (comprtype == this->compr) {
this->usecount++; this->usecount++;
spin_unlock(&jffs2_compressor_list_lock); spin_unlock(&jffs2_compressor_list_lock);
ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
spin_lock(&jffs2_compressor_list_lock); spin_lock(&jffs2_compressor_list_lock);
if (ret) { if (ret) {
printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);

View File

@ -49,9 +49,9 @@ struct jffs2_compressor {
char *name; char *name;
char compr; /* JFFS2_COMPR_XXX */ char compr; /* JFFS2_COMPR_XXX */
int (*compress)(unsigned char *data_in, unsigned char *cpage_out, int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *srclen, uint32_t *destlen, void *model); uint32_t *srclen, uint32_t *destlen);
int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
uint32_t cdatalen, uint32_t datalen, void *model); uint32_t cdatalen, uint32_t datalen);
int usecount; int usecount;
int disabled; /* if set the compressor won't compress */ int disabled; /* if set the compressor won't compress */
unsigned char *compr_buf; /* used by size compr. mode */ unsigned char *compr_buf; /* used by size compr. mode */

View File

@ -42,7 +42,7 @@ static int __init alloc_workspace(void)
} }
static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out, static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen, void *model) uint32_t *sourcelen, uint32_t *dstlen)
{ {
size_t compress_size; size_t compress_size;
int ret; int ret;
@ -67,7 +67,7 @@ static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
} }
static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen, void *model) uint32_t srclen, uint32_t destlen)
{ {
size_t dl = destlen; size_t dl = destlen;
int ret; int ret;

View File

@ -31,8 +31,7 @@
/* _compress returns the compressed size, -1 if bigger */ /* _compress returns the compressed size, -1 if bigger */
static int jffs2_rtime_compress(unsigned char *data_in, static int jffs2_rtime_compress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen, uint32_t *sourcelen, uint32_t *dstlen)
void *model)
{ {
short positions[256]; short positions[256];
int outpos = 0; int outpos = 0;
@ -73,8 +72,7 @@ static int jffs2_rtime_compress(unsigned char *data_in,
static int jffs2_rtime_decompress(unsigned char *data_in, static int jffs2_rtime_decompress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen, uint32_t srclen, uint32_t destlen)
void *model)
{ {
short positions[256]; short positions[256];
int outpos = 0; int outpos = 0;

View File

@ -298,7 +298,7 @@ static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in,
#if 0 #if 0
/* _compress returns the compressed size, -1 if bigger */ /* _compress returns the compressed size, -1 if bigger */
int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen, void *model) uint32_t *sourcelen, uint32_t *dstlen)
{ {
return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in,
cpage_out, sourcelen, dstlen); cpage_out, sourcelen, dstlen);
@ -306,8 +306,7 @@ int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out,
#endif #endif
static int jffs2_dynrubin_compress(unsigned char *data_in, static int jffs2_dynrubin_compress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen, uint32_t *sourcelen, uint32_t *dstlen)
void *model)
{ {
int bits[8]; int bits[8];
unsigned char histo[256]; unsigned char histo[256];
@ -387,8 +386,7 @@ static void rubin_do_decompress(int bit_divider, int *bits,
static int jffs2_rubinmips_decompress(unsigned char *data_in, static int jffs2_rubinmips_decompress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen, uint32_t sourcelen, uint32_t dstlen)
void *model)
{ {
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in,
cpage_out, sourcelen, dstlen); cpage_out, sourcelen, dstlen);
@ -397,8 +395,7 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in,
static int jffs2_dynrubin_decompress(unsigned char *data_in, static int jffs2_dynrubin_decompress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t sourcelen, uint32_t dstlen, uint32_t sourcelen, uint32_t dstlen)
void *model)
{ {
int bits[8]; int bits[8];
int c; int c;

View File

@ -68,8 +68,7 @@ static void free_workspaces(void)
static int jffs2_zlib_compress(unsigned char *data_in, static int jffs2_zlib_compress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen, uint32_t *sourcelen, uint32_t *dstlen)
void *model)
{ {
int ret; int ret;
@ -136,8 +135,7 @@ static int jffs2_zlib_compress(unsigned char *data_in,
static int jffs2_zlib_decompress(unsigned char *data_in, static int jffs2_zlib_decompress(unsigned char *data_in,
unsigned char *cpage_out, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen, uint32_t srclen, uint32_t destlen)
void *model)
{ {
int ret; int ret;
int wbits = MAX_WBITS; int wbits = MAX_WBITS;

View File

@ -367,7 +367,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
} }
/* We use f->target field to store the target path. */ /* We use f->target field to store the target path. */
f->target = kmalloc(targetlen + 1, GFP_KERNEL); f->target = kmemdup(target, targetlen + 1, GFP_KERNEL);
if (!f->target) { if (!f->target) {
printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
mutex_unlock(&f->sem); mutex_unlock(&f->sem);
@ -376,7 +376,6 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
goto fail; goto fail;
} }
memcpy(f->target, target, targetlen + 1);
D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target)); D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->target));
/* No data here. Only a metadata node, which will be /* No data here. Only a metadata node, which will be

View File

@ -151,7 +151,7 @@ int jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
} }
/* Be nice */ /* Be nice */
yield(); cond_resched();
mutex_lock(&c->erase_free_sem); mutex_lock(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
} }

View File

@ -474,6 +474,25 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
return inode; return inode;
} }
static int calculate_inocache_hashsize(uint32_t flash_size)
{
/*
* Pick a inocache hash size based on the size of the medium.
* Count how many megabytes we're dealing with, apply a hashsize twice
* that size, but rounding down to the usual big powers of 2. And keep
* to sensible bounds.
*/
int size_mb = flash_size / 1024 / 1024;
int hashsize = (size_mb * 2) & ~0x3f;
if (hashsize < INOCACHE_HASHSIZE_MIN)
return INOCACHE_HASHSIZE_MIN;
if (hashsize > INOCACHE_HASHSIZE_MAX)
return INOCACHE_HASHSIZE_MAX;
return hashsize;
}
int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
{ {
@ -520,7 +539,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
if (ret) if (ret)
return ret; return ret;
c->inocache_list = kcalloc(INOCACHE_HASHSIZE, sizeof(struct jffs2_inode_cache *), GFP_KERNEL); c->inocache_hashsize = calculate_inocache_hashsize(c->flash_size);
c->inocache_list = kcalloc(c->inocache_hashsize, sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
if (!c->inocache_list) { if (!c->inocache_list) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_wbuf; goto out_wbuf;

View File

@ -219,13 +219,14 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (!list_empty(&c->erase_complete_list) || if (!list_empty(&c->erase_complete_list) ||
!list_empty(&c->erase_pending_list)) { !list_empty(&c->erase_pending_list)) {
spin_unlock(&c->erase_completion_lock); spin_unlock(&c->erase_completion_lock);
mutex_unlock(&c->alloc_sem);
D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n")); D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() erasing pending blocks\n"));
if (jffs2_erase_pending_blocks(c, 1)) { if (jffs2_erase_pending_blocks(c, 1))
mutex_unlock(&c->alloc_sem);
return 0; return 0;
}
D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n")); D1(printk(KERN_DEBUG "No progress from erasing blocks; doing GC anyway\n"));
spin_lock(&c->erase_completion_lock); spin_lock(&c->erase_completion_lock);
mutex_lock(&c->alloc_sem);
} }
/* First, work out which block we're garbage-collecting */ /* First, work out which block we're garbage-collecting */

View File

@ -100,6 +100,7 @@ struct jffs2_sb_info {
wait_queue_head_t erase_wait; /* For waiting for erases to complete */ wait_queue_head_t erase_wait; /* For waiting for erases to complete */
wait_queue_head_t inocache_wq; wait_queue_head_t inocache_wq;
int inocache_hashsize;
struct jffs2_inode_cache **inocache_list; struct jffs2_inode_cache **inocache_list;
spinlock_t inocache_lock; spinlock_t inocache_lock;

View File

@ -420,7 +420,7 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t
{ {
struct jffs2_inode_cache *ret; struct jffs2_inode_cache *ret;
ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; ret = c->inocache_list[ino % c->inocache_hashsize];
while (ret && ret->ino < ino) { while (ret && ret->ino < ino) {
ret = ret->next; ret = ret->next;
} }
@ -441,7 +441,7 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
dbg_inocache("add %p (ino #%u)\n", new, new->ino); dbg_inocache("add %p (ino #%u)\n", new, new->ino);
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE]; prev = &c->inocache_list[new->ino % c->inocache_hashsize];
while ((*prev) && (*prev)->ino < new->ino) { while ((*prev) && (*prev)->ino < new->ino) {
prev = &(*prev)->next; prev = &(*prev)->next;
@ -462,7 +462,7 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
dbg_inocache("del %p (ino #%u)\n", old, old->ino); dbg_inocache("del %p (ino #%u)\n", old, old->ino);
spin_lock(&c->inocache_lock); spin_lock(&c->inocache_lock);
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE]; prev = &c->inocache_list[old->ino % c->inocache_hashsize];
while ((*prev) && (*prev)->ino < old->ino) { while ((*prev) && (*prev)->ino < old->ino) {
prev = &(*prev)->next; prev = &(*prev)->next;
@ -487,7 +487,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
int i; int i;
struct jffs2_inode_cache *this, *next; struct jffs2_inode_cache *this, *next;
for (i=0; i<INOCACHE_HASHSIZE; i++) { for (i=0; i < c->inocache_hashsize; i++) {
this = c->inocache_list[i]; this = c->inocache_list[i];
while (this) { while (this) {
next = this->next; next = this->next;

View File

@ -199,7 +199,8 @@ struct jffs2_inode_cache {
#define RAWNODE_CLASS_XATTR_DATUM 1 #define RAWNODE_CLASS_XATTR_DATUM 1
#define RAWNODE_CLASS_XATTR_REF 2 #define RAWNODE_CLASS_XATTR_REF 2
#define INOCACHE_HASHSIZE 128 #define INOCACHE_HASHSIZE_MIN 128
#define INOCACHE_HASHSIZE_MAX 1024
#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size) #define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)

View File

@ -20,7 +20,7 @@
#include "summary.h" #include "summary.h"
#include "debug.h" #include "debug.h"
#define DEFAULT_EMPTY_SCAN_SIZE 1024 #define DEFAULT_EMPTY_SCAN_SIZE 256
#define noisy_printk(noise, args...) do { \ #define noisy_printk(noise, args...) do { \
if (*(noise)) { \ if (*(noise)) { \
@ -435,7 +435,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) { unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
struct jffs2_unknown_node *node; struct jffs2_unknown_node *node;
struct jffs2_unknown_node crcnode; struct jffs2_unknown_node crcnode;
uint32_t ofs, prevofs; uint32_t ofs, prevofs, max_ofs;
uint32_t hdr_crc, buf_ofs, buf_len; uint32_t hdr_crc, buf_ofs, buf_len;
int err; int err;
int noise = 0; int noise = 0;
@ -550,12 +550,12 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
/* We temporarily use 'ofs' as a pointer into the buffer/jeb */ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */
ofs = 0; ofs = 0;
max_ofs = EMPTY_SCAN_SIZE(c->sector_size);
/* Scan only 4KiB of 0xFF before declaring it's empty */ /* Scan only EMPTY_SCAN_SIZE of 0xFF before declaring it's empty */
while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) while(ofs < max_ofs && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
ofs += 4; ofs += 4;
if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) { if (ofs == max_ofs) {
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
if (jffs2_cleanmarker_oob(c)) { if (jffs2_cleanmarker_oob(c)) {
/* scan oob, take care of cleanmarker */ /* scan oob, take care of cleanmarker */

View File

@ -84,7 +84,7 @@ struct nand_bbt_descr {
#define NAND_BBT_PERCHIP 0x00000080 #define NAND_BBT_PERCHIP 0x00000080
/* bbt has a version counter at offset veroffs */ /* bbt has a version counter at offset veroffs */
#define NAND_BBT_VERSION 0x00000100 #define NAND_BBT_VERSION 0x00000100
/* Create a bbt if none axists */ /* Create a bbt if none exists */
#define NAND_BBT_CREATE 0x00000200 #define NAND_BBT_CREATE 0x00000200
/* Search good / bad pattern through all pages of a block */ /* Search good / bad pattern through all pages of a block */
#define NAND_BBT_SCANALLPAGES 0x00000400 #define NAND_BBT_SCANALLPAGES 0x00000400
@ -102,6 +102,8 @@ struct nand_bbt_descr {
#define NAND_BBT_SCANBYTE1AND6 0x00100000 #define NAND_BBT_SCANBYTE1AND6 0x00100000
/* The nand_bbt_descr was created dynamicaly and must be freed */ /* The nand_bbt_descr was created dynamicaly and must be freed */
#define NAND_BBT_DYNAMICSTRUCT 0x00200000 #define NAND_BBT_DYNAMICSTRUCT 0x00200000
/* The bad block table does not OOB for marker */
#define NAND_BBT_NO_OOB 0x00400000
/* The maximum number of blocks to scan for a bbt */ /* The maximum number of blocks to scan for a bbt */
#define NAND_BBT_SCAN_MAXBLOCKS 4 #define NAND_BBT_SCAN_MAXBLOCKS 4

View File

@ -289,6 +289,7 @@ struct cfi_private {
must be of the same type. */ must be of the same type. */
int mfr, id; int mfr, id;
int numchips; int numchips;
map_word sector_erase_cmd;
unsigned long chipshift; /* Because they're of the same type */ unsigned long chipshift; /* Because they're of the same type */
const char *im_name; /* inter_module name for cmdset_setup */ const char *im_name; /* inter_module name for cmdset_setup */
struct flchip chips[0]; /* per-chip data structure for each chip */ struct flchip chips[0]; /* per-chip data structure for each chip */

181
include/linux/mtd/fsmc.h Normal file
View File

@ -0,0 +1,181 @@
/*
* incude/mtd/fsmc.h
*
* ST Microelectronics
* Flexible Static Memory Controller (FSMC)
* platform data interface and header file
*
* Copyright © 2010 ST Microelectronics
* Vipin Kumar <vipin.kumar@st.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MTD_FSMC_H
#define __MTD_FSMC_H
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
#include <linux/types.h>
#include <linux/mtd/partitions.h>
#include <asm/param.h>
#define FSMC_NAND_BW8 1
#define FSMC_NAND_BW16 2
/*
* The placement of the Command Latch Enable (CLE) and
* Address Latch Enable (ALE) is twised around in the
* SPEAR310 implementation.
*/
#if defined(CONFIG_MACH_SPEAR310)
#define PLAT_NAND_CLE (1 << 17)
#define PLAT_NAND_ALE (1 << 16)
#else
#define PLAT_NAND_CLE (1 << 16)
#define PLAT_NAND_ALE (1 << 17)
#endif
#define FSMC_MAX_NOR_BANKS 4
#define FSMC_MAX_NAND_BANKS 4
#define FSMC_FLASH_WIDTH8 1
#define FSMC_FLASH_WIDTH16 2
struct fsmc_nor_bank_regs {
uint32_t ctrl;
uint32_t ctrl_tim;
};
/* ctrl register definitions */
#define BANK_ENABLE (1 << 0)
#define MUXED (1 << 1)
#define NOR_DEV (2 << 2)
#define WIDTH_8 (0 << 4)
#define WIDTH_16 (1 << 4)
#define RSTPWRDWN (1 << 6)
#define WPROT (1 << 7)
#define WRT_ENABLE (1 << 12)
#define WAIT_ENB (1 << 13)
/* ctrl_tim register definitions */
struct fsms_nand_bank_regs {
uint32_t pc;
uint32_t sts;
uint32_t comm;
uint32_t attrib;
uint32_t ioata;
uint32_t ecc1;
uint32_t ecc2;
uint32_t ecc3;
};
#define FSMC_NOR_REG_SIZE 0x40
struct fsmc_regs {
struct fsmc_nor_bank_regs nor_bank_regs[FSMC_MAX_NOR_BANKS];
uint8_t reserved_1[0x40 - 0x20];
struct fsms_nand_bank_regs bank_regs[FSMC_MAX_NAND_BANKS];
uint8_t reserved_2[0xfe0 - 0xc0];
uint32_t peripid0; /* 0xfe0 */
uint32_t peripid1; /* 0xfe4 */
uint32_t peripid2; /* 0xfe8 */
uint32_t peripid3; /* 0xfec */
uint32_t pcellid0; /* 0xff0 */
uint32_t pcellid1; /* 0xff4 */
uint32_t pcellid2; /* 0xff8 */
uint32_t pcellid3; /* 0xffc */
};
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
/* pc register definitions */
#define FSMC_RESET (1 << 0)
#define FSMC_WAITON (1 << 1)
#define FSMC_ENABLE (1 << 2)
#define FSMC_DEVTYPE_NAND (1 << 3)
#define FSMC_DEVWID_8 (0 << 4)
#define FSMC_DEVWID_16 (1 << 4)
#define FSMC_ECCEN (1 << 6)
#define FSMC_ECCPLEN_512 (0 << 7)
#define FSMC_ECCPLEN_256 (1 << 7)
#define FSMC_TCLR_1 (1 << 9)
#define FSMC_TAR_1 (1 << 13)
/* sts register definitions */
#define FSMC_CODE_RDY (1 << 15)
/* comm register definitions */
#define FSMC_TSET_0 (0 << 0)
#define FSMC_TWAIT_6 (6 << 8)
#define FSMC_THOLD_4 (4 << 16)
#define FSMC_THIZ_1 (1 << 24)
/* peripid2 register definitions */
#define FSMC_REVISION_MSK (0xf)
#define FSMC_REVISION_SHFT (0x4)
#define FSMC_VER1 1
#define FSMC_VER2 2
#define FSMC_VER3 3
#define FSMC_VER4 4
#define FSMC_VER5 5
#define FSMC_VER6 6
#define FSMC_VER7 7
#define FSMC_VER8 8
static inline uint32_t get_fsmc_version(struct fsmc_regs *regs)
{
return (readl(&regs->peripid2) >> FSMC_REVISION_SHFT) &
FSMC_REVISION_MSK;
}
/*
* There are 13 bytes of ecc for every 512 byte block in FSMC version 8
* and it has to be read consecutively and immediately after the 512
* byte data block for hardware to generate the error bit offsets
* Managing the ecc bytes in the following way is easier. This way is
* similar to oobfree structure maintained already in u-boot nand driver
*/
#define MAX_ECCPLACE_ENTRIES 32
struct fsmc_nand_eccplace {
uint8_t offset;
uint8_t length;
};
struct fsmc_eccplace {
struct fsmc_nand_eccplace eccplace[MAX_ECCPLACE_ENTRIES];
};
/**
* fsmc_nand_platform_data - platform specific NAND controller config
* @partitions: partition table for the platform, use a default fallback
* if this is NULL
* @nr_partitions: the number of partitions in the previous entry
* @options: different options for the driver
* @width: bus width
* @bank: default bank
* @select_bank: callback to select a certain bank, this is
* platform-specific. If the controller only supports one bank
* this may be set to NULL
*/
struct fsmc_nand_platform_data {
struct mtd_partition *partitions;
unsigned int nr_partitions;
unsigned int options;
unsigned int width;
unsigned int bank;
void (*select_bank)(uint32_t bank, uint32_t busw);
};
extern int __init fsmc_nor_init(struct platform_device *pdev,
unsigned long base, uint32_t bank, uint32_t width);
extern void __init fsmc_init_board_info(struct platform_device *pdev,
struct mtd_partition *partitions, unsigned int nr_partitions,
unsigned int width);
#endif /* __MTD_FSMC_H */

View File

@ -37,14 +37,14 @@ struct INFTLrecord {
__u16 firstEUN; __u16 firstEUN;
__u16 lastEUN; __u16 lastEUN;
__u16 numfreeEUNs; __u16 numfreeEUNs;
__u16 LastFreeEUN; /* To speed up finding a free EUN */ __u16 LastFreeEUN; /* To speed up finding a free EUN */
int head,sect,cyl; int head,sect,cyl;
__u16 *PUtable; /* Physical Unit Table */ __u16 *PUtable; /* Physical Unit Table */
__u16 *VUtable; /* Virtual Unit Table */ __u16 *VUtable; /* Virtual Unit Table */
unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr; struct erase_info instr;
struct nand_ecclayout oobinfo; struct nand_ecclayout oobinfo;
}; };
int INFTL_mount(struct INFTLrecord *s); int INFTL_mount(struct INFTLrecord *s);

View File

@ -110,6 +110,21 @@ struct mtd_oob_ops {
uint8_t *oobbuf; uint8_t *oobbuf;
}; };
#define MTD_MAX_OOBFREE_ENTRIES_LARGE 32
#define MTD_MAX_ECCPOS_ENTRIES_LARGE 448
/*
* Internal ECC layout control structure. For historical reasons, there is a
* similar, smaller struct nand_ecclayout_user (in mtd-abi.h) that is retained
* for export to user-space via the ECCGETLAYOUT ioctl.
* nand_ecclayout should be expandable in the future simply by the above macros.
*/
struct nand_ecclayout {
__u32 eccbytes;
__u32 eccpos[MTD_MAX_ECCPOS_ENTRIES_LARGE];
__u32 oobavail;
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES_LARGE];
};
struct mtd_info { struct mtd_info {
u_char type; u_char type;
uint32_t flags; uint32_t flags;

View File

@ -27,15 +27,17 @@
struct mtd_info; struct mtd_info;
struct nand_flash_dev; struct nand_flash_dev;
/* Scan and identify a NAND device */ /* Scan and identify a NAND device */
extern int nand_scan (struct mtd_info *mtd, int max_chips); extern int nand_scan(struct mtd_info *mtd, int max_chips);
/* Separate phases of nand_scan(), allowing board driver to intervene /*
* and override command or ECC setup according to flash type */ * Separate phases of nand_scan(), allowing board driver to intervene
* and override command or ECC setup according to flash type.
*/
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips, extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
struct nand_flash_dev *table); struct nand_flash_dev *table);
extern int nand_scan_tail(struct mtd_info *mtd); extern int nand_scan_tail(struct mtd_info *mtd);
/* Free resources held by the NAND device */ /* Free resources held by the NAND device */
extern void nand_release (struct mtd_info *mtd); extern void nand_release(struct mtd_info *mtd);
/* Internal helper for board drivers which need to override command function */ /* Internal helper for board drivers which need to override command function */
extern void nand_wait_ready(struct mtd_info *mtd); extern void nand_wait_ready(struct mtd_info *mtd);
@ -49,12 +51,13 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
/* The maximum number of NAND chips in an array */ /* The maximum number of NAND chips in an array */
#define NAND_MAX_CHIPS 8 #define NAND_MAX_CHIPS 8
/* This constant declares the max. oobsize / page, which /*
* This constant declares the max. oobsize / page, which
* is supported now. If you add a chip with bigger oobsize/page * is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly. * adjust this accordingly.
*/ */
#define NAND_MAX_OOBSIZE 256 #define NAND_MAX_OOBSIZE 576
#define NAND_MAX_PAGESIZE 4096 #define NAND_MAX_PAGESIZE 8192
/* /*
* Constants for hardware specific CLE/ALE/NCE function * Constants for hardware specific CLE/ALE/NCE function
@ -88,6 +91,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
#define NAND_CMD_RNDIN 0x85 #define NAND_CMD_RNDIN 0x85
#define NAND_CMD_READID 0x90 #define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_PARAM 0xec
#define NAND_CMD_RESET 0xff #define NAND_CMD_RESET 0xff
#define NAND_CMD_LOCK 0x2a #define NAND_CMD_LOCK 0x2a
@ -152,9 +156,10 @@ typedef enum {
#define NAND_GET_DEVICE 0x80 #define NAND_GET_DEVICE 0x80
/* Option constants for bizarre disfunctionality and real /*
* features * Option constants for bizarre disfunctionality and real
*/ * features.
*/
/* Chip can not auto increment pages */ /* Chip can not auto increment pages */
#define NAND_NO_AUTOINCR 0x00000001 #define NAND_NO_AUTOINCR 0x00000001
/* Buswitdh is 16 bit */ /* Buswitdh is 16 bit */
@ -165,19 +170,27 @@ typedef enum {
#define NAND_CACHEPRG 0x00000008 #define NAND_CACHEPRG 0x00000008
/* Chip has copy back function */ /* Chip has copy back function */
#define NAND_COPYBACK 0x00000010 #define NAND_COPYBACK 0x00000010
/* AND Chip which has 4 banks and a confusing page / block /*
* assignment. See Renesas datasheet for further information */ * AND Chip which has 4 banks and a confusing page / block
* assignment. See Renesas datasheet for further information.
*/
#define NAND_IS_AND 0x00000020 #define NAND_IS_AND 0x00000020
/* Chip has a array of 4 pages which can be read without /*
* additional ready /busy waits */ * Chip has a array of 4 pages which can be read without
* additional ready /busy waits.
*/
#define NAND_4PAGE_ARRAY 0x00000040 #define NAND_4PAGE_ARRAY 0x00000040
/* Chip requires that BBT is periodically rewritten to prevent /*
* Chip requires that BBT is periodically rewritten to prevent
* bits from adjacent blocks from 'leaking' in altering data. * bits from adjacent blocks from 'leaking' in altering data.
* This happens with the Renesas AG-AND chips, possibly others. */ * This happens with the Renesas AG-AND chips, possibly others.
*/
#define BBT_AUTO_REFRESH 0x00000080 #define BBT_AUTO_REFRESH 0x00000080
/* Chip does not require ready check on read. True /*
* Chip does not require ready check on read. True
* for all large page devices, as they do not support * for all large page devices, as they do not support
* autoincrement.*/ * autoincrement.
*/
#define NAND_NO_READRDY 0x00000100 #define NAND_NO_READRDY 0x00000100
/* Chip does not allow subpage writes */ /* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE 0x00000200 #define NAND_NO_SUBPAGE_WRITE 0x00000200
@ -205,16 +218,27 @@ typedef enum {
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR) #define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
/* Non chip related options */ /* Non chip related options */
/* Use a flash based bad block table. This option is passed to the /*
* default bad block table function. */ * Use a flash based bad block table. OOB identifier is saved in OOB area.
* This option is passed to the default bad block table function.
*/
#define NAND_USE_FLASH_BBT 0x00010000 #define NAND_USE_FLASH_BBT 0x00010000
/* This option skips the bbt scan during initialization. */ /* This option skips the bbt scan during initialization. */
#define NAND_SKIP_BBTSCAN 0x00020000 #define NAND_SKIP_BBTSCAN 0x00020000
/* This option is defined if the board driver allocates its own buffers /*
(e.g. because it needs them DMA-coherent */ * This option is defined if the board driver allocates its own buffers
* (e.g. because it needs them DMA-coherent).
*/
#define NAND_OWN_BUFFERS 0x00040000 #define NAND_OWN_BUFFERS 0x00040000
/* Chip may not exist, so silence any errors in scan */ /* Chip may not exist, so silence any errors in scan */
#define NAND_SCAN_SILENT_NODEV 0x00080000 #define NAND_SCAN_SILENT_NODEV 0x00080000
/*
* If passed additionally to NAND_USE_FLASH_BBT then BBT code will not touch
* the OOB area.
*/
#define NAND_USE_FLASH_BBT_NO_OOB 0x00100000
/* Create an empty BBT with no vendor information if the BBT is available */
#define NAND_CREATE_EMPTY_BBT 0x00200000
/* Options set by nand scan */ /* Options set by nand scan */
/* Nand scan has allocated controller struct */ /* Nand scan has allocated controller struct */
@ -227,15 +251,80 @@ typedef enum {
/* Keep gcc happy */ /* Keep gcc happy */
struct nand_chip; struct nand_chip;
struct nand_onfi_params {
/* rev info and features block */
/* 'O' 'N' 'F' 'I' */
u8 sig[4];
__le16 revision;
__le16 features;
__le16 opt_cmd;
u8 reserved[22];
/* manufacturer information block */
char manufacturer[12];
char model[20];
u8 jedec_id;
__le16 date_code;
u8 reserved2[13];
/* memory organization block */
__le32 byte_per_page;
__le16 spare_bytes_per_page;
__le32 data_bytes_per_ppage;
__le16 spare_bytes_per_ppage;
__le32 pages_per_block;
__le32 blocks_per_lun;
u8 lun_count;
u8 addr_cycles;
u8 bits_per_cell;
__le16 bb_per_lun;
__le16 block_endurance;
u8 guaranteed_good_blocks;
__le16 guaranteed_block_endurance;
u8 programs_per_page;
u8 ppage_attr;
u8 ecc_bits;
u8 interleaved_bits;
u8 interleaved_ops;
u8 reserved3[13];
/* electrical parameter block */
u8 io_pin_capacitance_max;
__le16 async_timing_mode;
__le16 program_cache_timing_mode;
__le16 t_prog;
__le16 t_bers;
__le16 t_r;
__le16 t_ccs;
__le16 src_sync_timing_mode;
__le16 src_ssync_features;
__le16 clk_pin_capacitance_typ;
__le16 io_pin_capacitance_typ;
__le16 input_pin_capacitance_typ;
u8 input_pin_capacitance_max;
u8 driver_strenght_support;
__le16 t_int_r;
__le16 t_ald;
u8 reserved4[7];
/* vendor */
u8 reserved5[90];
__le16 crc;
} __attribute__((packed));
#define ONFI_CRC_BASE 0x4F4E
/** /**
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
* @lock: protection lock * @lock: protection lock
* @active: the mtd device which holds the controller currently * @active: the mtd device which holds the controller currently
* @wq: wait queue to sleep on if a NAND operation is in progress * @wq: wait queue to sleep on if a NAND operation is in
* used instead of the per chip wait queue when a hw controller is available * progress used instead of the per chip wait queue
* when a hw controller is available.
*/ */
struct nand_hw_control { struct nand_hw_control {
spinlock_t lock; spinlock_t lock;
struct nand_chip *active; struct nand_chip *active;
wait_queue_head_t wq; wait_queue_head_t wq;
}; };
@ -256,51 +345,42 @@ struct nand_hw_control {
* @correct: function for ecc correction, matching to ecc generator (sw/hw) * @correct: function for ecc correction, matching to ecc generator (sw/hw)
* @read_page_raw: function to read a raw page without ECC * @read_page_raw: function to read a raw page without ECC
* @write_page_raw: function to write a raw page without ECC * @write_page_raw: function to write a raw page without ECC
* @read_page: function to read a page according to the ecc generator requirements * @read_page: function to read a page according to the ecc generator
* requirements.
* @read_subpage: function to read parts of the page covered by ECC. * @read_subpage: function to read parts of the page covered by ECC.
* @write_page: function to write a page according to the ecc generator requirements * @write_page: function to write a page according to the ecc generator
* requirements.
* @read_oob: function to read chip OOB data * @read_oob: function to read chip OOB data
* @write_oob: function to write chip OOB data * @write_oob: function to write chip OOB data
*/ */
struct nand_ecc_ctrl { struct nand_ecc_ctrl {
nand_ecc_modes_t mode; nand_ecc_modes_t mode;
int steps; int steps;
int size; int size;
int bytes; int bytes;
int total; int total;
int prepad; int prepad;
int postpad; int postpad;
struct nand_ecclayout *layout; struct nand_ecclayout *layout;
void (*hwctl)(struct mtd_info *mtd, int mode); void (*hwctl)(struct mtd_info *mtd, int mode);
int (*calculate)(struct mtd_info *mtd, int (*calculate)(struct mtd_info *mtd, const uint8_t *dat,
const uint8_t *dat, uint8_t *ecc_code);
uint8_t *ecc_code); int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
int (*correct)(struct mtd_info *mtd, uint8_t *dat, uint8_t *calc_ecc);
uint8_t *read_ecc, int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *calc_ecc); uint8_t *buf, int page);
int (*read_page_raw)(struct mtd_info *mtd, void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
struct nand_chip *chip, const uint8_t *buf);
uint8_t *buf, int page); int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
void (*write_page_raw)(struct mtd_info *mtd, uint8_t *buf, int page);
struct nand_chip *chip, int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf); uint32_t offs, uint32_t len, uint8_t *buf);
int (*read_page)(struct mtd_info *mtd, void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
struct nand_chip *chip, const uint8_t *buf);
uint8_t *buf, int page); int (*read_oob)(struct mtd_info *mtd, struct nand_chip *chip, int page,
int (*read_subpage)(struct mtd_info *mtd, int sndcmd);
struct nand_chip *chip, int (*write_oob)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, int page);
uint8_t *buf);
void (*write_page)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
int (*read_oob)(struct mtd_info *mtd,
struct nand_chip *chip,
int page,
int sndcmd);
int (*write_oob)(struct mtd_info *mtd,
struct nand_chip *chip,
int page);
}; };
/** /**
@ -320,102 +400,132 @@ struct nand_buffers {
/** /**
* struct nand_chip - NAND Private Flash Chip Data * struct nand_chip - NAND Private Flash Chip Data
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device * flash device
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the
* flash device.
* @read_byte: [REPLACEABLE] read one byte from the chip * @read_byte: [REPLACEABLE] read one byte from the chip
* @read_word: [REPLACEABLE] read one word from the chip * @read_word: [REPLACEABLE] read one word from the chip
* @write_buf: [REPLACEABLE] write data from the buffer to the chip * @write_buf: [REPLACEABLE] write data from the buffer to the chip
* @read_buf: [REPLACEABLE] read data from the chip into the buffer * @read_buf: [REPLACEABLE] read data from the chip into the buffer
* @verify_buf: [REPLACEABLE] verify buffer contents against the chip data * @verify_buf: [REPLACEABLE] verify buffer contents against the chip
* data.
* @select_chip: [REPLACEABLE] select chip nr * @select_chip: [REPLACEABLE] select chip nr
* @block_bad: [REPLACEABLE] check, if the block is bad * @block_bad: [REPLACEABLE] check, if the block is bad
* @block_markbad: [REPLACEABLE] mark the block bad * @block_markbad: [REPLACEABLE] mark the block bad
* @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling
* ALE/CLE/nCE. Also used to write command and address * ALE/CLE/nCE. Also used to write command and address
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line * @init_size: [BOARDSPECIFIC] hardwarespecific funtion for setting
* If set to NULL no access to ready/busy is available and the ready/busy information * mtd->oobsize, mtd->writesize and so on.
* is read from the chip status register * @id_data contains the 8 bytes values of NAND_CMD_READID.
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip * Return with the bus width.
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing
* device ready/busy line. If set to NULL no access to
* ready/busy is available and the ready/busy information
* is read from the chip status register.
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing
* commands to the chip.
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
* ready.
* @ecc: [BOARDSPECIFIC] ecc control ctructure * @ecc: [BOARDSPECIFIC] ecc control ctructure
* @buffers: buffer structure for read/write * @buffers: buffer structure for read/write
* @hwcontrol: platform-specific hardware control structure * @hwcontrol: platform-specific hardware control structure
* @ops: oob operation operands * @ops: oob operation operands
* @erase_cmd: [INTERN] erase command write function, selectable due to AND support * @erase_cmd: [INTERN] erase command write function, selectable due
* to AND support.
* @scan_bbt: [REPLACEABLE] function to scan bad block table * @scan_bbt: [REPLACEABLE] function to scan bad block table
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering
* data from array to read regs (tR).
* @state: [INTERN] the current state of the NAND device * @state: [INTERN] the current state of the NAND device
* @oob_poi: poison value buffer * @oob_poi: poison value buffer
* @page_shift: [INTERN] number of address bits in a page (column address bits) * @page_shift: [INTERN] number of address bits in a page (column
* address bits).
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
* @chip_shift: [INTERN] number of address bits in one chip * @chip_shift: [INTERN] number of address bits in one chip
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about * @options: [BOARDSPECIFIC] various chip options. They can partly
* special functionality. See the defines for further explanation * be set to inform nand_scan about special functionality.
* @badblockpos: [INTERN] position of the bad block marker in the oob area * See the defines for further explanation.
* @badblockpos: [INTERN] position of the bad block marker in the oob
* area.
* @cellinfo: [INTERN] MLC/multichip data from chip ident * @cellinfo: [INTERN] MLC/multichip data from chip ident
* @numchips: [INTERN] number of physical chips * @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays * @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf * @pagebuf: [INTERN] holds the pagenumber which is currently in
* data_buf.
* @subpagesize: [INTERN] holds the subpagesize * @subpagesize: [INTERN] holds the subpagesize
* @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded),
* non 0 if ONFI supported.
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
* supported, 0 otherwise.
* @ecclayout: [REPLACEABLE] the default ecc placement scheme * @ecclayout: [REPLACEABLE] the default ecc placement scheme
* @bbt: [INTERN] bad block table pointer * @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_td: [REPLACEABLE] bad block table descriptor for flash
* lookup.
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
* @controller: [REPLACEABLE] a pointer to a hardware controller structure * bad block scan.
* which is shared among multiple independend devices * @controller: [REPLACEABLE] a pointer to a hardware controller
* structure which is shared among multiple independend
* devices.
* @priv: [OPTIONAL] pointer to private chip date * @priv: [OPTIONAL] pointer to private chip date
* @errstat: [OPTIONAL] hardware specific function to perform additional error status checks * @errstat: [OPTIONAL] hardware specific function to perform
* (determine if errors are correctable) * additional error status checks (determine if errors are
* correctable).
* @write_page: [REPLACEABLE] High-level page write function * @write_page: [REPLACEABLE] High-level page write function
*/ */
struct nand_chip { struct nand_chip {
void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W; void __iomem *IO_ADDR_W;
uint8_t (*read_byte)(struct mtd_info *mtd); uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip); void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
unsigned int ctrl); int (*init_size)(struct mtd_info *mtd, struct nand_chip *this,
int (*dev_ready)(struct mtd_info *mtd); u8 *id_data);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int (*dev_ready)(struct mtd_info *mtd);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,
void (*erase_cmd)(struct mtd_info *mtd, int page); int page_addr);
int (*scan_bbt)(struct mtd_info *mtd); int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, int (*scan_bbt)(struct mtd_info *mtd);
const uint8_t *buf, int page, int cached, int raw); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int page, int cached, int raw);
int chip_delay; int chip_delay;
unsigned int options; unsigned int options;
int page_shift; int page_shift;
int phys_erase_shift; int phys_erase_shift;
int bbt_erase_shift; int bbt_erase_shift;
int chip_shift; int chip_shift;
int numchips; int numchips;
uint64_t chipsize; uint64_t chipsize;
int pagemask; int pagemask;
int pagebuf; int pagebuf;
int subpagesize; int subpagesize;
uint8_t cellinfo; uint8_t cellinfo;
int badblockpos; int badblockpos;
int badblockbits; int badblockbits;
flstate_t state; int onfi_version;
struct nand_onfi_params onfi_params;
uint8_t *oob_poi; flstate_t state;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout; uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc; struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers; struct nand_buffers *buffers;
@ -423,13 +533,13 @@ struct nand_chip {
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
uint8_t *bbt; uint8_t *bbt;
struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern; struct nand_bbt_descr *badblock_pattern;
void *priv; void *priv;
}; };
/* /*
@ -473,7 +583,7 @@ struct nand_flash_dev {
*/ */
struct nand_manufacturers { struct nand_manufacturers {
int id; int id;
char * name; char *name;
}; };
extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_flash_dev nand_flash_ids[];
@ -486,7 +596,7 @@ extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt); int allowbbt);
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);
/** /**
* struct platform_nand_chip - chip level device structure * struct platform_nand_chip - chip level device structure
@ -502,17 +612,16 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
* @priv: hardware controller specific settings * @priv: hardware controller specific settings
*/ */
struct platform_nand_chip { struct platform_nand_chip {
int nr_chips; int nr_chips;
int chip_offset; int chip_offset;
int nr_partitions; int nr_partitions;
struct mtd_partition *partitions; struct mtd_partition *partitions;
struct nand_ecclayout *ecclayout; struct nand_ecclayout *ecclayout;
int chip_delay; int chip_delay;
unsigned int options; unsigned int options;
const char **part_probe_types; const char **part_probe_types;
void (*set_parts)(uint64_t size, void (*set_parts)(uint64_t size, struct platform_nand_chip *chip);
struct platform_nand_chip *chip); void *priv;
void *priv;
}; };
/* Keep gcc happy */ /* Keep gcc happy */
@ -534,18 +643,15 @@ struct platform_device;
* All fields are optional and depend on the hardware driver requirements * All fields are optional and depend on the hardware driver requirements
*/ */
struct platform_nand_ctrl { struct platform_nand_ctrl {
int (*probe)(struct platform_device *pdev); int (*probe)(struct platform_device *pdev);
void (*remove)(struct platform_device *pdev); void (*remove)(struct platform_device *pdev);
void (*hwcontrol)(struct mtd_info *mtd, int cmd); void (*hwcontrol)(struct mtd_info *mtd, int cmd);
int (*dev_ready)(struct mtd_info *mtd); int (*dev_ready)(struct mtd_info *mtd);
void (*select_chip)(struct mtd_info *mtd, int chip); void (*select_chip)(struct mtd_info *mtd, int chip);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat, void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);
unsigned int ctrl); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*write_buf)(struct mtd_info *mtd, void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
const uint8_t *buf, int len); void *priv;
void (*read_buf)(struct mtd_info *mtd,
uint8_t *buf, int len);
void *priv;
}; };
/** /**
@ -554,8 +660,8 @@ struct platform_nand_ctrl {
* @ctrl: controller level device structure * @ctrl: controller level device structure
*/ */
struct platform_nand_data { struct platform_nand_data {
struct platform_nand_chip chip; struct platform_nand_chip chip;
struct platform_nand_ctrl ctrl; struct platform_nand_ctrl ctrl;
}; };
/* Some helpers to access the data structures */ /* Some helpers to access the data structures */

View File

@ -39,7 +39,7 @@ struct mtd_partition {
uint64_t size; /* partition size */ uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */ uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */ uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */
}; };
#define MTDPART_OFS_NXTBLK (-2) #define MTDPART_OFS_NXTBLK (-2)
@ -89,4 +89,9 @@ static inline int mtd_has_cmdlinepart(void) { return 1; }
static inline int mtd_has_cmdlinepart(void) { return 0; } static inline int mtd_has_cmdlinepart(void) { return 0; }
#endif #endif
int mtd_is_master(struct mtd_info *mtd);
int mtd_add_partition(struct mtd_info *master, char *name,
long long offset, long long length);
int mtd_del_partition(struct mtd_info *master, int partno);
#endif #endif

View File

@ -52,6 +52,7 @@ struct mtd_oob_buf64 {
#define MTD_NANDFLASH 4 #define MTD_NANDFLASH 4
#define MTD_DATAFLASH 6 #define MTD_DATAFLASH 6
#define MTD_UBIVOLUME 7 #define MTD_UBIVOLUME 7
#define MTD_MLCNANDFLASH 8
#define MTD_WRITEABLE 0x400 /* Device is writeable */ #define MTD_WRITEABLE 0x400 /* Device is writeable */
#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
@ -119,7 +120,7 @@ struct otp_info {
#define OTPGETREGIONCOUNT _IOW('M', 14, int) #define OTPGETREGIONCOUNT _IOW('M', 14, int)
#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info) #define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
#define OTPLOCK _IOR('M', 16, struct otp_info) #define OTPLOCK _IOR('M', 16, struct otp_info)
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout_user)
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
#define MTDFILEMODE _IO('M', 19) #define MTDFILEMODE _IO('M', 19)
#define MEMERASE64 _IOW('M', 20, struct erase_info_user64) #define MEMERASE64 _IOW('M', 20, struct erase_info_user64)
@ -144,13 +145,18 @@ struct nand_oobfree {
}; };
#define MTD_MAX_OOBFREE_ENTRIES 8 #define MTD_MAX_OOBFREE_ENTRIES 8
#define MTD_MAX_ECCPOS_ENTRIES 64
/* /*
* ECC layout control structure. Exported to userspace for * OBSOLETE: ECC layout control structure. Exported to user-space via ioctl
* diagnosis and to allow creation of raw images * ECCGETLAYOUT for backwards compatbility and should not be mistaken as a
* complete set of ECC information. The ioctl truncates the larger internal
* structure to retain binary compatibility with the static declaration of the
* ioctl. Note that the "MTD_MAX_..._ENTRIES" macros represent the max size of
* the user struct, not the MAX size of the internal struct nand_ecclayout.
*/ */
struct nand_ecclayout { struct nand_ecclayout_user {
__u32 eccbytes; __u32 eccbytes;
__u32 eccpos[64]; __u32 eccpos[MTD_MAX_ECCPOS_ENTRIES];
__u32 oobavail; __u32 oobavail;
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES]; struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
}; };

View File

@ -29,6 +29,6 @@ typedef struct mtd_info_user mtd_info_t;
typedef struct erase_info_user erase_info_t; typedef struct erase_info_user erase_info_t;
typedef struct region_info_user region_info_t; typedef struct region_info_user region_info_t;
typedef struct nand_oobinfo nand_oobinfo_t; typedef struct nand_oobinfo nand_oobinfo_t;
typedef struct nand_ecclayout nand_ecclayout_t; typedef struct nand_ecclayout_user nand_ecclayout_t;
#endif /* __MTD_USER_H__ */ #endif /* __MTD_USER_H__ */