提交 79346507 编写于 作者: L Linus Torvalds

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
......@@ -66,7 +66,7 @@ static DEFINE_SPINLOCK(syscon_resetreg_lock);
* AMBA bus
* |
* +- CPU
* +- NANDIF NAND Flash interface
* +- FSMC NANDIF NAND Flash interface
* +- SEMI Shared Memory interface
* +- ISP Image Signal Processor (U335 only)
* +- CDS (U335 only)
......@@ -726,7 +726,7 @@ static struct clk cpu_clk = {
};
static struct clk nandif_clk = {
.name = "NANDIF",
.name = "FSMC",
.parent = &amba_clk,
.hw_ctrld = false,
.reset = true,
......@@ -1259,7 +1259,7 @@ static struct clk_lookup lookups[] = {
/* Connected directly to the AMBA bus */
DEF_LOOKUP("amba", &amba_clk),
DEF_LOOKUP("cpu", &cpu_clk),
DEF_LOOKUP("fsmc", &nandif_clk),
DEF_LOOKUP("fsmc-nand", &nandif_clk),
DEF_LOOKUP("semi", &semi_clk),
#ifdef CONFIG_MACH_U300_BS335
DEF_LOOKUP("isp", &isp_clk),
......
......@@ -21,7 +21,8 @@
#include <linux/gpio.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <mach/coh901318.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/fsmc.h>
#include <asm/types.h>
#include <asm/setup.h>
......@@ -30,6 +31,7 @@
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
#include <mach/coh901318.h>
#include <mach/hardware.h>
#include <mach/syscon.h>
#include <mach/dma_channels.h>
......@@ -285,6 +287,13 @@ static struct resource rtc_resources[] = {
*/
static struct resource fsmc_resources[] = {
{
.name = "nand_data",
.start = U300_NAND_CS0_PHYS_BASE,
.end = U300_NAND_CS0_PHYS_BASE + SZ_16K - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "fsmc_regs",
.start = U300_NAND_IF_PHYS_BASE,
.end = U300_NAND_IF_PHYS_BASE + SZ_4K - 1,
.flags = IORESOURCE_MEM,
......@@ -1429,11 +1438,39 @@ static struct platform_device rtc_device = {
.resource = rtc_resources,
};
static struct platform_device fsmc_device = {
.name = "nandif",
static struct mtd_partition u300_partitions[] = {
{
.name = "bootrecords",
.offset = 0,
.size = SZ_128K,
},
{
.name = "free",
.offset = SZ_128K,
.size = 8064 * SZ_1K,
},
{
.name = "platform",
.offset = 8192 * SZ_1K,
.size = 253952 * SZ_1K,
},
};
static struct fsmc_nand_platform_data nand_platform_data = {
.partitions = u300_partitions,
.nr_partitions = ARRAY_SIZE(u300_partitions),
.options = NAND_SKIP_BBTSCAN,
.width = FSMC_NAND_BW8,
};
static struct platform_device nand_device = {
.name = "fsmc-nand",
.id = -1,
.num_resources = ARRAY_SIZE(fsmc_resources),
.resource = fsmc_resources,
.num_resources = ARRAY_SIZE(fsmc_resources),
.dev = {
.platform_data = &nand_platform_data,
},
};
static struct platform_device ave_device = {
......@@ -1465,7 +1502,7 @@ static struct platform_device *platform_devs[] __initdata = {
&keypad_device,
&rtc_device,
&gpio_device,
&fsmc_device,
&nand_device,
&wdog_device,
&ave_device
};
......
......@@ -20,11 +20,9 @@
/* NAND Flash CS0 */
#define U300_NAND_CS0_PHYS_BASE 0x80000000
#define U300_NAND_CS0_VIRT_BASE 0xff040000
/* NFIF */
#define U300_NAND_IF_PHYS_BASE 0x9f800000
#define U300_NAND_IF_VIRT_BASE 0xff030000
/* AHB Peripherals */
#define U300_AHB_PER_PHYS_BASE 0xa0000000
......
......@@ -30,15 +30,15 @@ struct pxa3xx_nand_cmdset {
};
struct pxa3xx_nand_flash {
const struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
const struct pxa3xx_nand_cmdset *cmdset;
uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
uint32_t page_size; /* Page size in bytes (PAGE_SZ) */
uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */
uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */
uint32_t num_blocks; /* Number of physical blocks in Flash */
uint32_t chip_id;
uint32_t chip_id;
unsigned int page_per_block; /* Pages per block (PG_PER_BLK) */
unsigned int page_size; /* Page size in bytes (PAGE_SZ) */
unsigned int flash_width; /* Width of Flash memory (DWIDTH_M) */
unsigned int dfc_width; /* Width of flash controller(DWIDTH_C) */
unsigned int num_blocks; /* Number of physical blocks in Flash */
struct pxa3xx_nand_cmdset *cmdset; /* NAND command set */
struct pxa3xx_nand_timing *timing; /* NAND Flash timing */
};
struct pxa3xx_nand_platform_data {
......
#ifndef __BCM963XX_TAG_H
#define __BCM963XX_TAG_H
#define TAGVER_LEN 4 /* Length of Tag Version */
#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */
#define SIG1_LEN 20 /* Company Signature 1 Length */
#define SIG2_LEN 14 /* Company Signature 2 Lenght */
#define BOARDID_LEN 16 /* Length of BoardId */
#define ENDIANFLAG_LEN 2 /* Endian Flag Length */
#define CHIPID_LEN 6 /* Chip Id Length */
#define IMAGE_LEN 10 /* Length of Length Field */
#define ADDRESS_LEN 12 /* Length of Address field */
#define DUALFLAG_LEN 2 /* Dual Image flag Length */
#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */
#define RSASIG_LEN 20 /* Length of RSA Signature in tag */
#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */
#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */
#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */
#define CRC_LEN 4 /* Length of CRC in bytes */
#define ALTTAGINFO_LEN 54 /* Alternate length for vendor information; Pirelli */
#define NUM_PIRELLI 2
#define IMAGETAG_CRC_START 0xFFFFFFFF
#define PIRELLI_BOARDS { \
"AGPF-S0", \
"DWV-S0", \
}
/*
* The broadcom firmware assumes the rootfs starts the image,
* therefore uses the rootfs start (flash_image_address)
* to determine where to flash the image. Since we have the kernel first
* we have to give it the kernel address, but the crc uses the length
* associated with this address (root_length), which is added to the kernel
* length (kernel_length) to determine the length of image to flash and thus
* needs to be rootfs + deadcode (jffs2 EOF marker)
*/
struct bcm_tag {
/* 0-3: Version of the image tag */
char tag_version[TAGVER_LEN];
/* 4-23: Company Line 1 */
char sig_1[SIG1_LEN];
/* 24-37: Company Line 2 */
char sig_2[SIG2_LEN];
/* 38-43: Chip this image is for */
char chip_id[CHIPID_LEN];
/* 44-59: Board name */
char board_id[BOARDID_LEN];
/* 60-61: Map endianness -- 1 BE 0 LE */
char big_endian[ENDIANFLAG_LEN];
/* 62-71: Total length of image */
char total_length[IMAGE_LEN];
/* 72-83: Address in memory of CFE */
char cfe__address[ADDRESS_LEN];
/* 84-93: Size of CFE */
char cfe_length[IMAGE_LEN];
/* 94-105: Address in memory of image start
* (kernel for OpenWRT, rootfs for stock firmware)
*/
char flash_image_start[ADDRESS_LEN];
/* 106-115: Size of rootfs */
char root_length[IMAGE_LEN];
/* 116-127: Address in memory of kernel */
char kernel_address[ADDRESS_LEN];
/* 128-137: Size of kernel */
char kernel_length[IMAGE_LEN];
/* 138-139: Unused at the moment */
char dual_image[DUALFLAG_LEN];
/* 140-141: Unused at the moment */
char inactive_flag[INACTIVEFLAG_LEN];
/* 142-161: RSA Signature (not used; some vendors may use this) */
char rsa_signature[RSASIG_LEN];
/* 162-191: Compilation and related information (not used in OpenWrt) */
char information1[TAGINFO1_LEN];
/* 192-195: Version flash layout */
char flash_layout_ver[FLASHLAYOUTVER_LEN];
/* 196-199: kernel+rootfs CRC32 */
char fskernel_crc[CRC_LEN];
/* 200-215: Unused except on Alice Gate where is is information */
char information2[TAGINFO2_LEN];
/* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */
char image_crc[CRC_LEN];
/* 220-223: CRC32 of rootfs partition */
char rootfs_crc[CRC_LEN];
/* 224-227: CRC32 of kernel partition */
char kernel_crc[CRC_LEN];
/* 228-235: Unused at present */
char reserved1[8];
/* 236-239: CRC32 of header excluding tagVersion */
char header_crc[CRC_LEN];
/* 240-255: Unused at present */
char reserved2[16];
};
#endif /* __BCM63XX_TAG_H */
......@@ -682,9 +682,12 @@ config 4xx_SOC
bool
config FSL_LBC
bool
bool "Freescale Local Bus support"
depends on FSL_SOC
help
Freescale Localbus support
Enables reporting of errors from the Freescale local bus
controller. Also contains some common code used by
drivers for specific local bus peripherals.
config FSL_GTM
bool
......
/* Freescale Local Bus Controller
*
* Copyright (c) 2006-2007 Freescale Semiconductor
* Copyright © 2006-2007, 2010 Freescale Semiconductor
*
* Authors: Nick Spence <nick.spence@freescale.com>,
* Scott Wood <scottwood@freescale.com>
* Jack Lan <jack.lan@freescale.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -26,6 +27,8 @@
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/spinlock.h>
struct fsl_lbc_bank {
__be32 br; /**< Base Register */
......@@ -125,13 +128,23 @@ struct fsl_lbc_regs {
#define LTESR_ATMW 0x00800000
#define LTESR_ATMR 0x00400000
#define LTESR_CS 0x00080000
#define LTESR_UPM 0x00000002
#define LTESR_CC 0x00000001
#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
#define LTESR_MASK (LTESR_BM | LTESR_FCT | LTESR_PAR | LTESR_WP \
| LTESR_ATMW | LTESR_ATMR | LTESR_CS | LTESR_UPM \
| LTESR_CC)
#define LTESR_CLEAR 0xFFFFFFFF
#define LTECCR_CLEAR 0xFFFFFFFF
#define LTESR_STATUS LTESR_MASK
#define LTEIR_ENABLE LTESR_MASK
#define LTEDR_ENABLE 0x00000000
__be32 ltedr; /**< Transfer Error Disable Register */
__be32 lteir; /**< Transfer Error Interrupt Register */
__be32 lteatr; /**< Transfer Error Attributes Register */
__be32 ltear; /**< Transfer Error Address Register */
u8 res6[0xC];
__be32 lteccr; /**< Transfer Error ECC Register */
u8 res6[0x8];
__be32 lbcr; /**< Configuration Register */
#define LBCR_LDIS 0x80000000
#define LBCR_LDIS_SHIFT 31
......@@ -235,6 +248,7 @@ struct fsl_upm {
int width;
};
extern u32 fsl_lbc_addr(phys_addr_t addr_base);
extern int fsl_lbc_find(phys_addr_t addr_base);
extern int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm);
......@@ -265,7 +279,23 @@ static inline void fsl_upm_end_pattern(struct fsl_upm *upm)
cpu_relax();
}
/* overview of the fsl lbc controller */
struct fsl_lbc_ctrl {
/* device info */
struct device *dev;
struct fsl_lbc_regs __iomem *regs;
int irq;
wait_queue_head_t irq_wait;
spinlock_t lock;
void *nand;
/* status read from LTESR by irq handler */
unsigned int irq_status;
};
extern int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base,
u32 mar);
extern struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
#endif /* __ASM_FSL_LBC_H */
/*
* Freescale LBC and UPM routines.
*
* Copyright (c) 2007-2008 MontaVista Software, Inc.
* Copyright © 2007-2008 MontaVista Software, Inc.
* Copyright © 2010 Freescale Semiconductor
*
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
* Author: Jack Lan <Jack.Lan@freescale.com>
* Author: Roy Zang <tie-fei.zang@freescale.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -19,39 +22,37 @@
#include <linux/types.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <asm/prom.h>
#include <asm/fsl_lbc.h>
static spinlock_t fsl_lbc_lock = __SPIN_LOCK_UNLOCKED(fsl_lbc_lock);
static struct fsl_lbc_regs __iomem *fsl_lbc_regs;
struct fsl_lbc_ctrl *fsl_lbc_ctrl_dev;
EXPORT_SYMBOL(fsl_lbc_ctrl_dev);
static char __initdata *compat_lbc[] = {
"fsl,pq2-localbus",
"fsl,pq2pro-localbus",
"fsl,pq3-localbus",
"fsl,elbc",
};
static int __init fsl_lbc_init(void)
/**
* fsl_lbc_addr - convert the base address
* @addr_base: base address of the memory bank
*
* This function converts a base address of lbc into the right format for the
* BR register. If the SOC has eLBC then it returns 32bit physical address
* else it convers a 34bit local bus physical address to correct format of
* 32bit address for BR register (Example: MPC8641).
*/
u32 fsl_lbc_addr(phys_addr_t addr_base)
{
struct device_node *lbus;
int i;
struct device_node *np = fsl_lbc_ctrl_dev->dev->of_node;
u32 addr = addr_base & 0xffff8000;
for (i = 0; i < ARRAY_SIZE(compat_lbc); i++) {
lbus = of_find_compatible_node(NULL, NULL, compat_lbc[i]);
if (lbus)
goto found;
}
return -ENODEV;
if (of_device_is_compatible(np, "fsl,elbc"))
return addr;
found:
fsl_lbc_regs = of_iomap(lbus, 0);
of_node_put(lbus);
if (!fsl_lbc_regs)
return -ENOMEM;
return 0;
return addr | ((addr_base & 0x300000000ull) >> 19);
}
arch_initcall(fsl_lbc_init);
EXPORT_SYMBOL(fsl_lbc_addr);
/**
* fsl_lbc_find - find Localbus bank
......@@ -65,15 +66,17 @@ arch_initcall(fsl_lbc_init);
int fsl_lbc_find(phys_addr_t addr_base)
{
int i;
struct fsl_lbc_regs __iomem *lbc;
if (!fsl_lbc_regs)
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(fsl_lbc_regs->bank); i++) {
__be32 br = in_be32(&fsl_lbc_regs->bank[i].br);
__be32 or = in_be32(&fsl_lbc_regs->bank[i].or);
lbc = fsl_lbc_ctrl_dev->regs;
for (i = 0; i < ARRAY_SIZE(lbc->bank); i++) {
__be32 br = in_be32(&lbc->bank[i].br);
__be32 or = in_be32(&lbc->bank[i].or);
if (br & BR_V && (br & or & BR_BA) == addr_base)
if (br & BR_V && (br & or & BR_BA) == fsl_lbc_addr(addr_base))
return i;
}
......@@ -94,22 +97,27 @@ int fsl_upm_find(phys_addr_t addr_base, struct fsl_upm *upm)
{
int bank;
__be32 br;
struct fsl_lbc_regs __iomem *lbc;
bank = fsl_lbc_find(addr_base);
if (bank < 0)
return bank;
br = in_be32(&fsl_lbc_regs->bank[bank].br);
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
lbc = fsl_lbc_ctrl_dev->regs;
br = in_be32(&lbc->bank[bank].br);
switch (br & BR_MSEL) {
case BR_MS_UPMA:
upm->mxmr = &fsl_lbc_regs->mamr;
upm->mxmr = &lbc->mamr;
break;
case BR_MS_UPMB:
upm->mxmr = &fsl_lbc_regs->mbmr;
upm->mxmr = &lbc->mbmr;
break;
case BR_MS_UPMC:
upm->mxmr = &fsl_lbc_regs->mcmr;
upm->mxmr = &lbc->mcmr;
break;
default:
return -EINVAL;
......@@ -148,9 +156,12 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
int ret = 0;
unsigned long flags;
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
return -ENODEV;
spin_lock_irqsave(&fsl_lbc_lock, flags);
out_be32(&fsl_lbc_regs->mar, mar);
out_be32(&fsl_lbc_ctrl_dev->regs->mar, mar);
switch (upm->width) {
case 8:
......@@ -172,3 +183,166 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
return ret;
}
EXPORT_SYMBOL(fsl_upm_run_pattern);
static int __devinit fsl_lbc_ctrl_init(struct fsl_lbc_ctrl *ctrl)
{
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
/* clear event registers */
setbits32(&lbc->ltesr, LTESR_CLEAR);
out_be32(&lbc->lteatr, 0);
out_be32(&lbc->ltear, 0);
out_be32(&lbc->lteccr, LTECCR_CLEAR);
out_be32(&lbc->ltedr, LTEDR_ENABLE);
/* Enable interrupts for any detected events */
out_be32(&lbc->lteir, LTEIR_ENABLE);
return 0;
}
/*
* NOTE: This interrupt is used to report localbus events of various kinds,
* such as transaction errors on the chipselects.
*/
static irqreturn_t fsl_lbc_ctrl_irq(int irqno, void *data)
{
struct fsl_lbc_ctrl *ctrl = data;
struct fsl_lbc_regs __iomem *lbc = ctrl->regs;
u32 status;
status = in_be32(&lbc->ltesr);
if (!status)
return IRQ_NONE;
out_be32(&lbc->ltesr, LTESR_CLEAR);
out_be32(&lbc->lteatr, 0);
out_be32(&lbc->ltear, 0);
ctrl->irq_status = status;
if (status & LTESR_BM)
dev_err(ctrl->dev, "Local bus monitor time-out: "
"LTESR 0x%08X\n", status);
if (status & LTESR_WP)
dev_err(ctrl->dev, "Write protect error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_ATMW)
dev_err(ctrl->dev, "Atomic write error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_ATMR)
dev_err(ctrl->dev, "Atomic read error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_CS)
dev_err(ctrl->dev, "Chip select error: "
"LTESR 0x%08X\n", status);
if (status & LTESR_UPM)
;
if (status & LTESR_FCT) {
dev_err(ctrl->dev, "FCM command time-out: "
"LTESR 0x%08X\n", status);
smp_wmb();
wake_up(&ctrl->irq_wait);
}
if (status & LTESR_PAR) {
dev_err(ctrl->dev, "Parity or Uncorrectable ECC error: "
"LTESR 0x%08X\n", status);
smp_wmb();
wake_up(&ctrl->irq_wait);
}
if (status & LTESR_CC) {
smp_wmb();
wake_up(&ctrl->irq_wait);
}
if (status & ~LTESR_MASK)
dev_err(ctrl->dev, "Unknown error: "
"LTESR 0x%08X\n", status);
return IRQ_HANDLED;
}
/*
* fsl_lbc_ctrl_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code allocates all of
* the resources needed for the controller only. The
* resources for the NAND banks themselves are allocated
* in the chip probe function.
*/
static int __devinit fsl_lbc_ctrl_probe(struct platform_device *dev)
{
int ret;
if (!dev->dev.of_node) {
dev_err(&dev->dev, "Device OF-Node is NULL");
return -EFAULT;
}
fsl_lbc_ctrl_dev = kzalloc(sizeof(*fsl_lbc_ctrl_dev), GFP_KERNEL);
if (!fsl_lbc_ctrl_dev)
return -ENOMEM;
dev_set_drvdata(&dev->dev, fsl_lbc_ctrl_dev);
spin_lock_init(&fsl_lbc_ctrl_dev->lock);
init_waitqueue_head(&fsl_lbc_ctrl_dev->irq_wait);
fsl_lbc_ctrl_dev->regs = of_iomap(dev->dev.of_node, 0);
if (!fsl_lbc_ctrl_dev->regs) {
dev_err(&dev->dev, "failed to get memory region\n");
ret = -ENODEV;
goto err;
}
fsl_lbc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
if (fsl_lbc_ctrl_dev->irq == NO_IRQ) {
dev_err(&dev->dev, "failed to get irq resource\n");
ret = -ENODEV;
goto err;
}
fsl_lbc_ctrl_dev->dev = &dev->dev;
ret = fsl_lbc_ctrl_init(fsl_lbc_ctrl_dev);
if (ret < 0)
goto err;
ret = request_irq(fsl_lbc_ctrl_dev->irq, fsl_lbc_ctrl_irq, 0,
"fsl-lbc", fsl_lbc_ctrl_dev);
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_lbc_ctrl_dev->irq);
ret = fsl_lbc_ctrl_dev->irq;
goto err;
}
return 0;
err:
iounmap(fsl_lbc_ctrl_dev->regs);
kfree(fsl_lbc_ctrl_dev);
return ret;
}
static const struct of_device_id fsl_lbc_match[] = {
{ .compatible = "fsl,elbc", },
{ .compatible = "fsl,pq3-localbus", },
{ .compatible = "fsl,pq2-localbus", },
{ .compatible = "fsl,pq2pro-localbus", },
{},
};
static struct platform_driver fsl_lbc_ctrl_driver = {
.driver = {
.name = "fsl-lbc",
.of_match_table = fsl_lbc_match,
},
.probe = fsl_lbc_ctrl_probe,
};
static int __init fsl_lbc_init(void)
{
return platform_driver_register(&fsl_lbc_ctrl_driver);
}
module_init(fsl_lbc_init);
......@@ -1496,7 +1496,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
switch (mode) {
case FL_WRITING:
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0x40) : CMD(0x41);
break;
case FL_OTP_WRITE:
write_cmd = CMD(0xc0);
......@@ -1661,7 +1661,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
cmd_adr = adr & ~(wbufsize-1);
/* Let's determine this according to the interleave only once */
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
write_cmd = (cfi->cfiq->P_ID != P_ID_INTEL_PERFORMANCE) ? CMD(0xe8) : CMD(0xe9);
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, cmd_adr, FL_WRITING);
......
......@@ -291,6 +291,23 @@ static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param)
cfi->addr_unlock1 = 0x555;
cfi->addr_unlock2 = 0x2AA;
cfi->sector_erase_cmd = CMD(0x50);
}
static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
fixup_sst39vf_rev_b(mtd, param);
/*
* CFI reports 1024 sectors (0x03ff+1) of 64KBytes (0x0100*256) where
* it should report a size of 8KBytes (0x0020*256).
*/
cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
pr_warning("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", mtd->name);
}
static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param)
......@@ -317,14 +334,14 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
/* Used to fix CFI-Tables of chips without Extended Query Tables */
static struct cfi_fixup cfi_nopri_fixup_table[] = {
{ CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602
{ CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601
{ CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202
{ CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201
{ CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B
{ CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B
{ CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B
{ CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B
{ CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, /* SST39VF1602 */
{ CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, /* SST39VF1601 */
{ CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, /* SST39VF3202 */
{ CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, /* SST39VF3201 */
{ CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3202B */
{ CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF3201B */
{ CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6402B */
{ CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, /* SST39VF6401B */
{ 0, 0, NULL, NULL }
};
......@@ -344,6 +361,10 @@ static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
{ CFI_MFR_SST, 0x536A, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6402 */
{ CFI_MFR_SST, 0x536B, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6401 */
{ CFI_MFR_SST, 0x536C, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6404 */
{ CFI_MFR_SST, 0x536D, fixup_sst38vf640x_sectorsize, NULL, }, /* SST38VF6403 */
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif
......@@ -374,6 +395,13 @@ static void cfi_fixup_major_minor(struct cfi_private *cfi,
if (cfi->mfr == CFI_MFR_SAMSUNG && cfi->id == 0x257e &&
extp->MajorVersion == '0')
extp->MajorVersion = '1';
/*
* SST 38VF640x chips report major=0xFF / minor=0xFF.
*/
if (cfi->mfr == CFI_MFR_SST && (cfi->id >> 4) == 0x0536) {
extp->MajorVersion = '1';
extp->MinorVersion = '0';
}
}
struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
......@@ -545,15 +573,6 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
goto setup_err;
}
#if 0
// debug
for (i=0; i<mtd->numeraseregions;i++){
printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
i,mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].numblocks);
}
#endif
__module_get(THIS_MODULE);
register_reboot_notifier(&mtd->reboot_notifier);
......@@ -674,7 +693,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
* there was an error (so leave the erase
* routine to recover from it) or we trying to
* use the erase-in-progress sector. */
map_write(map, CMD(0x30), chip->in_progress_block_addr);
map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
chip->state = FL_ERASING;
chip->oldstate = FL_READY;
printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
......@@ -727,7 +746,7 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
switch(chip->oldstate) {
case FL_ERASING:
chip->state = chip->oldstate;
map_write(map, CMD(0x30), chip->in_progress_block_addr);
map_write(map, cfi->sector_erase_cmd, chip->in_progress_block_addr);
chip->oldstate = FL_READY;
chip->state = FL_ERASING;
break;
......@@ -870,7 +889,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
local_irq_disable();
/* Resume the write or erase operation */
map_write(map, CMD(0x30), adr);
map_write(map, cfi->sector_erase_cmd, adr);
chip->state = oldstate;
start = xip_currtime();
} else if (usec >= 1000000/HZ) {
......@@ -1025,9 +1044,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
mutex_lock(&chip->mutex);
if (chip->state != FL_READY){
#if 0
printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
#endif
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
......@@ -1035,10 +1051,6 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
schedule();
remove_wait_queue(&chip->wq, &wait);
#if 0
if(signal_pending(current))
return -EINTR;
#endif
timeo = jiffies + HZ;
goto retry;
......@@ -1246,9 +1258,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
mutex_lock(&cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) {
#if 0
printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
#endif
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
......@@ -1256,10 +1265,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
#if 0
if(signal_pending(current))
return -EINTR;
#endif
goto retry;
}
......@@ -1324,9 +1329,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
mutex_lock(&cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) {
#if 0
printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
#endif
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
......@@ -1334,10 +1336,6 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
#if 0
if(signal_pending(current))
return -EINTR;
#endif
goto retry1;
}
......@@ -1396,7 +1394,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
/* Write Buffer Load */
map_write(map, CMD(0x25), cmd_adr);
......@@ -1675,7 +1672,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
map_write(map, CMD(0x30), adr);
map_write(map, cfi->sector_erase_cmd, adr);
chip->state = FL_ERASING;
chip->erase_suspended = 0;
......
......@@ -177,6 +177,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
cfi->cfi_mode = CFI_MODE_CFI;
cfi->sector_erase_cmd = CMD(0x30);
/* Read the CFI info structure */
xip_disable_qry(base, map, cfi);
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
......
......@@ -75,6 +75,13 @@ int __xipram cfi_qry_mode_on(uint32_t base, struct map_info *map,
cfi_send_gen_cmd(0xAA, 0x5555, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, 0x2AAA, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x5555, base, map, cfi, cfi->device_type, NULL);
if (cfi_qry_present(map, base, cfi))
return 1;
/* SST 39VF640xB */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xAA, 0x555, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, 0x2AA, base, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x98, 0x555, base, map, cfi, cfi->device_type, NULL);
if (cfi_qry_present(map, base, cfi))
return 1;
/* QRY not found */
......
......@@ -91,7 +91,6 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
} else
instr->state = MTD_ERASE_DONE;
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return err;
}
......
......@@ -661,11 +661,14 @@ static const struct spi_device_id m25p_ids[] = {
{ "s25sl008a", INFO(0x010213, 0, 64 * 1024, 16, 0) },
{ "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) },
{ "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) },
{ "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, SECT_4K) },
{ "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) },
{ "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
{ "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
{ "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, 0) },
{ "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, 0) },
{ "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) },
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) },
......@@ -714,6 +717,7 @@ static const struct spi_device_id m25p_ids[] = {
{ "w25x32", INFO(0xef3016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25q32", INFO(0xef4016, 0, 64 * 1024, 64, SECT_4K) },
{ "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) },
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
/* Catalyst / On Semiconductor -- non-JEDEC */
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1) },
......@@ -924,7 +928,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
nr_parts = data->nr_parts;
}
#ifdef CONFIG_OF
#ifdef CONFIG_MTD_OF_PARTS
if (nr_parts <= 0 && spi->dev.of_node) {
nr_parts = of_mtd_parse_partitions(&spi->dev,
spi->dev.of_node, &parts);
......
......@@ -15,7 +15,7 @@
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
*/
#define pr_fmt(fmt) "phram: " fmt
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <asm/io.h>
#include <linux/init.h>
......
......@@ -251,6 +251,15 @@ config MTD_NETtel
help
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
config MTD_BCM963XX
tristate "Map driver for Broadcom BCM963xx boards"
depends on BCM63XX
select MTD_MAP_BANK_WIDTH_2
select MTD_CFI_I1
help
Support for parsing CFE image tag and creating MTD partitions on
Broadcom BCM63xx boards.
config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC"
depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN
......
......@@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o
/*
* Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org>
* Mike Albon <malbon@openwrt.org>
* Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/vmalloc.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <asm/mach-bcm63xx/bcm963xx_tag.h>
#define BCM63XX_BUSWIDTH 2 /* Buswidth */
#define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */
#define PFX KBUILD_MODNAME ": "
static struct mtd_partition *parsed_parts;
static struct mtd_info *bcm963xx_mtd_info;
static struct map_info bcm963xx_map = {
.name = "bcm963xx",
.bankwidth = BCM63XX_BUSWIDTH,
};
static int parse_cfe_partitions(struct mtd_info *master,
struct mtd_partition **pparts)
{
/* CFE, NVRAM and global Linux are always present */
int nrparts = 3, curpart = 0;
struct bcm_tag *buf;
struct mtd_partition *parts;
int ret;
size_t retlen;
unsigned int rootfsaddr, kerneladdr, spareaddr;
unsigned int rootfslen, kernellen, sparelen, totallen;
int namelen = 0;
int i;
char *boardid;
char *tagversion;
/* Allocate memory for buffer */
buf = vmalloc(sizeof(struct bcm_tag));
if (!buf)
return -ENOMEM;
/* Get the tag */
ret = master->read(master, master->erasesize, sizeof(struct bcm_tag),
&retlen, (void *)buf);
if (retlen != sizeof(struct bcm_tag)) {
vfree(buf);
return -EIO;
}
sscanf(buf->kernel_address, "%u", &kerneladdr);
sscanf(buf->kernel_length, "%u", &kernellen);
sscanf(buf->total_length, "%u", &totallen);
tagversion = &(buf->tag_version[0]);
boardid = &(buf->board_id[0]);
printk(KERN_INFO PFX "CFE boot tag found with version %s "
"and board type %s\n", tagversion, boardid);
kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE;
rootfsaddr = kerneladdr + kernellen;
spareaddr = roundup(totallen, master->erasesize) + master->erasesize;
sparelen = master->size - spareaddr - master->erasesize;
rootfslen = spareaddr - rootfsaddr;
/* Determine number of partitions */
namelen = 8;
if (rootfslen > 0) {
nrparts++;
namelen += 6;
};
if (kernellen > 0) {
nrparts++;
namelen += 6;
};
/* Ask kernel for more memory */
parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL);
if (!parts) {
vfree(buf);
return -ENOMEM;
};
/* Start building partition list */
parts[curpart].name = "CFE";
parts[curpart].offset = 0;
parts[curpart].size = master->erasesize;
curpart++;
if (kernellen > 0) {
parts[curpart].name = "kernel";
parts[curpart].offset = kerneladdr;
parts[curpart].size = kernellen;
curpart++;
};
if (rootfslen > 0) {
parts[curpart].name = "rootfs";
parts[curpart].offset = rootfsaddr;
parts[curpart].size = rootfslen;
if (sparelen > 0)
parts[curpart].size += sparelen;
curpart++;
};
parts[curpart].name = "nvram";
parts[curpart].offset = master->size - master->erasesize;
parts[curpart].size = master->erasesize;
/* Global partition "linux" to make easy firmware upgrade */
curpart++;
parts[curpart].name = "linux";
parts[curpart].offset = parts[0].size;
parts[curpart].size = master->size - parts[0].size - parts[3].size;
for (i = 0; i < nrparts; i++)
printk(KERN_INFO PFX "Partition %d is %s offset %lx and "
"length %lx\n", i, parts[i].name,
(long unsigned int)(parts[i].offset),
(long unsigned int)(parts[i].size));
printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n",
spareaddr, sparelen);
*pparts = parts;
vfree(buf);
return nrparts;
};
static int bcm963xx_detect_cfe(struct mtd_info *master)
{
int idoffset = 0x4e0;
static char idstring[8] = "CFE1CFE1";
char buf[9];
int ret;
size_t retlen;
ret = master->read(master, idoffset, 8, &retlen, (void *)buf);
buf[retlen] = 0;
printk(KERN_INFO PFX "Read Signature value of %s\n", buf);
return strncmp(idstring, buf, 8);
}
static int bcm963xx_probe(struct platform_device *pdev)
{
int err = 0;
int parsed_nr_parts = 0;
char *part_type;
struct resource *r;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&pdev->dev, "no resource supplied\n");
return -ENODEV;
}
bcm963xx_map.phys = r->start;
bcm963xx_map.size = resource_size(r);
bcm963xx_map.virt = ioremap(r->start, resource_size(r));
if (!bcm963xx_map.virt) {
dev_err(&pdev->dev, "failed to ioremap\n");
return -EIO;
}
dev_info(&pdev->dev, "0x%08lx at 0x%08x\n",
bcm963xx_map.size, bcm963xx_map.phys);
simple_map_init(&bcm963xx_map);
bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map);
if (!bcm963xx_mtd_info) {
dev_err(&pdev->dev, "failed to probe using CFI\n");
err = -EIO;
goto err_probe;
}
bcm963xx_mtd_info->owner = THIS_MODULE;
/* This is mutually exclusive */
if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) {
dev_info(&pdev->dev, "CFE bootloader detected\n");
if (parsed_nr_parts == 0) {
int ret = parse_cfe_partitions(bcm963xx_mtd_info,
&parsed_parts);
if (ret > 0) {
part_type = "CFE";
parsed_nr_parts = ret;
}
}
} else {
dev_info(&pdev->dev, "unsupported bootloader\n");
err = -ENODEV;
goto err_probe;
}
return add_mtd_partitions(bcm963xx_mtd_info, parsed_parts,
parsed_nr_parts);
err_probe:
iounmap(bcm963xx_map.virt);
return err;
}
static int bcm963xx_remove(struct platform_device *pdev)
{
if (bcm963xx_mtd_info) {
del_mtd_partitions(bcm963xx_mtd_info);
map_destroy(bcm963xx_mtd_info);
}
if (bcm963xx_map.virt) {
iounmap(bcm963xx_map.virt);
bcm963xx_map.virt = 0;
}
return 0;
}
static struct platform_driver bcm63xx_mtd_dev = {
.probe = bcm963xx_probe,
.remove = bcm963xx_remove,
.driver = {
.name = "bcm963xx-flash",
.owner = THIS_MODULE,
},
};
static int __init bcm963xx_mtd_init(void)
{
return platform_driver_register(&bcm63xx_mtd_dev);
}
static void __exit bcm963xx_mtd_exit(void)
{
platform_driver_unregister(&bcm63xx_mtd_dev);
}
module_init(bcm963xx_mtd_init);
module_exit(bcm963xx_mtd_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot");
MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>");
MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>");
......@@ -208,10 +208,14 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
if (!state)
return -ENOMEM;
/*
* We cast start/end to known types in the boards file, so cast
* away their pointer types here to the known types (gpios->xxx).
*/
state->gpio_count = gpios->end;
state->gpio_addrs = (void *)gpios->start;
state->gpio_addrs = (void *)(unsigned long)gpios->start;
state->gpio_values = (void *)(state + 1);
state->win_size = memory->end - memory->start + 1;
state->win_size = resource_size(memory);
memset(state->gpio_values, 0xff, arr_size);
state->map.name = DRIVER_NAME;
......@@ -221,7 +225,7 @@ static int __devinit gpio_flash_probe(struct platform_device *pdev)
state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width;
state->map.size = state->win_size * (1 << state->gpio_count);
state->map.virt = (void __iomem *)memory->start;
state->map.virt = ioremap_nocache(memory->start, state->map.size);
state->map.phys = NO_XIP;
state->map.map_priv_1 = (unsigned long)state;
......
......@@ -640,10 +640,6 @@ static int pcmciamtd_config(struct pcmcia_device *link)
}
dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name);
return 0;
dev_err(&dev->p_dev->dev, "CS Error, exiting\n");
pcmciamtd_release(link);
return -ENODEV;
}
......
......@@ -50,7 +50,7 @@ static int parse_obsolete_partitions(struct platform_device *dev,
{
int i, plen, nr_parts;
const struct {
u32 offset, len;
__be32 offset, len;
} *part;
const char *names;
......@@ -69,9 +69,9 @@ static int parse_obsolete_partitions(struct platform_device *dev,
names = of_get_property(dp, "partition-names", &plen);
for (i = 0; i < nr_parts; i++) {
info->parts[i].offset = part->offset;
info->parts[i].size = part->len & ~1;
if (part->len & 1) /* bit 0 set signifies read only partition */
info->parts[i].offset = be32_to_cpu(part->offset);
info->parts[i].size = be32_to_cpu(part->len) & ~1;
if (be32_to_cpu(part->len) & 1) /* bit 0 set signifies read only partition */
info->parts[i].mask_flags = MTD_WRITEABLE;
if (names && (plen > 0)) {
......@@ -226,11 +226,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
struct resource res;
struct of_flash *info;
const char *probe_type = match->data;
const u32 *width;
const __be32 *width;
int err;
int i;
int count;
const u32 *p;
const __be32 *p;
int reg_tuple_size;
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
......@@ -267,9 +267,11 @@ static int __devinit of_flash_probe(struct platform_device *dev,
for (i = 0; i < count; i++) {
err = -ENXIO;
if (of_address_to_resource(dp, i, &res)) {
dev_err(&dev->dev, "Can't get IO address from device"
" tree\n");
goto err_out;
/*
* Continue with next register tuple if this
* one is not mappable
*/
continue;
}
dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n",
......
......@@ -37,7 +37,6 @@
#include "mtdcore.h"
static DEFINE_MUTEX(mtd_blkdevs_mutex);
static LIST_HEAD(blktrans_majors);
static DEFINE_MUTEX(blktrans_ref_mutex);
......@@ -133,6 +132,10 @@ static int mtd_blktrans_thread(void *arg)
if (!req && !(req = blk_fetch_request(rq))) {
set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
set_current_state(TASK_RUNNING);
spin_unlock_irq(rq->queue_lock);
schedule();
spin_lock_irq(rq->queue_lock);
......@@ -176,54 +179,53 @@ static void mtd_blktrans_request(struct request_queue *rq)
static int blktrans_open(struct block_device *bdev, fmode_t mode)
{
struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
int ret;
int ret = 0;
if (!dev)
return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/
mutex_lock(&mtd_blkdevs_mutex);
mutex_lock(&dev->lock);
if (!dev->mtd) {
ret = -ENXIO;
if (dev->open++)
goto unlock;
}
ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
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);
}
/* Take another reference on the device so it won't go away till
last release */
if (!ret)
kref_get(&dev->ref);
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
mutex_unlock(&mtd_blkdevs_mutex);
return ret;
}
static int blktrans_release(struct gendisk *disk, fmode_t mode)
{
struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
int ret = -ENXIO;
int ret = 0;
if (!dev)
return ret;
mutex_lock(&mtd_blkdevs_mutex);
mutex_lock(&dev->lock);
/* Release one reference, we sure its not the last one here*/
kref_put(&dev->ref, blktrans_dev_release);
if (!dev->mtd)
if (--dev->open)
goto unlock;
ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
kref_put(&dev->ref, blktrans_dev_release);
module_put(dev->tr->owner);
if (dev->mtd) {
ret = dev->tr->release ? dev->tr->release(dev) : 0;
__put_mtd_device(dev->mtd);
}
unlock:
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
mutex_unlock(&mtd_blkdevs_mutex);
return ret;
}
......@@ -256,7 +258,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
if (!dev)
return ret;
mutex_lock(&mtd_blkdevs_mutex);
mutex_lock(&dev->lock);
if (!dev->mtd)
......@@ -271,7 +272,6 @@ static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
}
unlock:
mutex_unlock(&dev->lock);
mutex_unlock(&mtd_blkdevs_mutex);
blktrans_dev_put(dev);
return ret;
}
......@@ -385,9 +385,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
gd->queue = new->rq;
__get_mtd_device(new->mtd);
__module_get(tr->owner);
/* Create processing thread */
/* TODO: workqueue ? */
new->thread = kthread_run(mtd_blktrans_thread, new,
......@@ -410,8 +407,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
}
return 0;
error4:
module_put(tr->owner);
__put_mtd_device(new->mtd);
blk_cleanup_queue(new->rq);
error3:
put_disk(new->disk);
......@@ -448,17 +443,15 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
blk_start_queue(old->rq);
spin_unlock_irqrestore(&old->queue_lock, flags);
/* Ask trans driver for release to the mtd device */
/* If the device is currently open, tell trans driver to close it,
then put mtd device, and don't touch it again */
mutex_lock(&old->lock);
if (old->open && old->tr->release) {
old->tr->release(old);
old->open = 0;
if (old->open) {
if (old->tr->release)
old->tr->release(old);
__put_mtd_device(old->mtd);
}
__put_mtd_device(old->mtd);
module_put(old->tr->owner);
/* At that point, we don't touch the mtd anymore */
old->mtd = NULL;
mutex_unlock(&old->lock);
......@@ -508,13 +501,16 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
mutex_lock(&mtd_table_mutex);
ret = register_blkdev(tr->major, tr->name);
if (ret) {
if (ret < 0) {
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
tr->name, tr->major, ret);
mutex_unlock(&mtd_table_mutex);
return ret;
}
if (ret)
tr->major = ret;
tr->blkshift = ffs(tr->blksize) - 1;
INIT_LIST_HEAD(&tr->devs);
......
......@@ -30,8 +30,9 @@
#include <linux/backing-dev.h>
#include <linux/compat.h>
#include <linux/mount.h>
#include <linux/blkpg.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/map.h>
#include <asm/uaccess.h>
......@@ -478,6 +479,78 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
return ret;
}
/*
* Copies (and truncates, if necessary) data from the larger struct,
* nand_ecclayout, to the smaller, deprecated layout struct,
* nand_ecclayout_user. This is necessary only to suppport the deprecated
* API ioctl ECCGETLAYOUT while allowing all new functionality to use
* nand_ecclayout flexibly (i.e. the struct may change size in new
* releases without requiring major rewrites).
*/
static int shrink_ecclayout(const struct nand_ecclayout *from,
struct nand_ecclayout_user *to)
{
int i;
if (!from || !to)
return -EINVAL;
memset(to, 0, sizeof(*to));
to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
for (i = 0; i < to->eccbytes; i++)
to->eccpos[i] = from->eccpos[i];
for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
if (from->oobfree[i].length == 0 &&
from->oobfree[i].offset == 0)
break;
to->oobavail += from->oobfree[i].length;
to->oobfree[i] = from->oobfree[i];
}
return 0;
}
#ifdef CONFIG_MTD_PARTITIONS
static int mtd_blkpg_ioctl(struct mtd_info *mtd,
struct blkpg_ioctl_arg __user *arg)
{
struct blkpg_ioctl_arg a;
struct blkpg_partition p;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
/* Only master mtd device must be used to control partitions */
if (!mtd_is_master(mtd))
return -EINVAL;
if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
return -EFAULT;
if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
return -EFAULT;
switch (a.op) {
case BLKPG_ADD_PARTITION:
return mtd_add_partition(mtd, p.devname, p.start, p.length);
case BLKPG_DEL_PARTITION:
if (p.pno < 0)
return -EINVAL;
return mtd_del_partition(mtd, p.pno);
default:
return -EINVAL;
}
}
#endif
static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
{
struct mtd_file_info *mfi = file->private_data;
......@@ -514,6 +587,9 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
if (get_user(ur_idx, &(ur->regionindex)))
return -EFAULT;
if (ur_idx >= mtd->numeraseregions)
return -EINVAL;
kr = &(mtd->eraseregions[ur_idx]);
if (put_user(kr->offset, &(ur->offset))
......@@ -813,14 +889,23 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
}
#endif
/* This ioctl is being deprecated - it truncates the ecc layout */
case ECCGETLAYOUT:
{
struct nand_ecclayout_user *usrlay;
if (!mtd->ecclayout)
return -EOPNOTSUPP;
if (copy_to_user(argp, mtd->ecclayout,
sizeof(struct nand_ecclayout)))
return -EFAULT;
usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
if (!usrlay)
return -ENOMEM;
shrink_ecclayout(mtd->ecclayout, usrlay);
if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
ret = -EFAULT;
kfree(usrlay);
break;
}
......@@ -856,6 +941,22 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
break;
}
#ifdef CONFIG_MTD_PARTITIONS
case BLKPG:
{
ret = mtd_blkpg_ioctl(mtd,
(struct blkpg_ioctl_arg __user *)arg);
break;
}
case BLKRRPART:
{
/* No reread partition feature. Just return ok */
ret = 0;
break;
}
#endif
default:
ret = -ENOTTY;
}
......@@ -1033,7 +1134,7 @@ static const struct file_operations mtd_fops = {
static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
}
static struct file_system_type mtd_inodefs_type = {
......
......@@ -29,9 +29,11 @@
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/err.h>
/* Our partition linked list */
static LIST_HEAD(mtd_partitions);
static DEFINE_MUTEX(mtd_partitions_mutex);
/* Our partition node structure */
struct mtd_part {
......@@ -326,6 +328,12 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
return res;
}
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
kfree(p);
}
/*
* This function unregisters and destroy all slave MTD objects which are
* attached to the given master MTD object.
......@@ -334,33 +342,42 @@ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
int del_mtd_partitions(struct mtd_info *master)
{
struct mtd_part *slave, *next;
int ret, err = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if (slave->master == master) {
ret = del_mtd_device(&slave->mtd);
if (ret < 0) {
err = ret;
continue;
}
list_del(&slave->list);
del_mtd_device(&slave->mtd);
kfree(slave);
free_partition(slave);
}
mutex_unlock(&mtd_partitions_mutex);
return 0;
return err;
}
EXPORT_SYMBOL(del_mtd_partitions);
static struct mtd_part *add_one_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
static struct mtd_part *allocate_partition(struct mtd_info *master,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
{
struct mtd_part *slave;
char *name;
/* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
if (!slave) {
name = kstrdup(part->name, GFP_KERNEL);
if (!name || !slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
master->name);
del_mtd_partitions(master);
return NULL;
master->name);
kfree(name);
kfree(slave);
return ERR_PTR(-ENOMEM);
}
list_add(&slave->list, &mtd_partitions);
/* set up the MTD object for this partition */
slave->mtd.type = master->type;
......@@ -371,7 +388,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.oobavail = master->oobavail;
slave->mtd.subpage_sft = master->subpage_sft;
slave->mtd.name = part->name;
slave->mtd.name = name;
slave->mtd.owner = master->owner;
slave->mtd.backing_dev_info = master->backing_dev_info;
......@@ -518,12 +535,89 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
}
out_register:
/* register our partition */
add_mtd_device(&slave->mtd);
return slave;
}
int mtd_add_partition(struct mtd_info *master, char *name,
long long offset, long long length)
{
struct mtd_partition part;
struct mtd_part *p, *new;
uint64_t start, end;
int ret = 0;
/* the direct offset is expected */
if (offset == MTDPART_OFS_APPEND ||
offset == MTDPART_OFS_NXTBLK)
return -EINVAL;
if (length == MTDPART_SIZ_FULL)
length = master->size - offset;
if (length <= 0)
return -EINVAL;
part.name = name;
part.size = length;
part.offset = offset;
part.mask_flags = 0;
part.ecclayout = NULL;
new = allocate_partition(master, &part, -1, offset);
if (IS_ERR(new))
return PTR_ERR(new);
start = offset;
end = offset + length;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry(p, &mtd_partitions, list)
if (p->master == master) {
if ((start >= p->offset) &&
(start < (p->offset + p->mtd.size)))
goto err_inv;
if ((end >= p->offset) &&
(end < (p->offset + p->mtd.size)))
goto err_inv;
}
list_add(&new->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
add_mtd_device(&new->mtd);
return ret;
err_inv:
mutex_unlock(&mtd_partitions_mutex);
free_partition(new);
return -EINVAL;
}
EXPORT_SYMBOL_GPL(mtd_add_partition);
int mtd_del_partition(struct mtd_info *master, int partno)
{
struct mtd_part *slave, *next;
int ret = -EINVAL;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if ((slave->master == master) &&
(slave->mtd.index == partno)) {
ret = del_mtd_device(&slave->mtd);
if (ret < 0)
break;
list_del(&slave->list);
free_partition(slave);
break;
}
mutex_unlock(&mtd_partitions_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(mtd_del_partition);
/*
* This function, given a master MTD object and a partition table, creates
* and registers slave MTD objects which are bound to the master according to
......@@ -544,9 +638,16 @@ int add_mtd_partitions(struct mtd_info *master,
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = add_one_partition(master, parts + i, i, cur_offset);
if (!slave)
return -ENOMEM;
slave = allocate_partition(master, parts + i, i, cur_offset);
if (IS_ERR(slave))
return PTR_ERR(slave);
mutex_lock(&mtd_partitions_mutex);
list_add(&slave->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
add_mtd_device(&slave->mtd);
cur_offset = slave->offset + slave->mtd.size;
}
......@@ -618,3 +719,20 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types,
return ret;
}
EXPORT_SYMBOL_GPL(parse_mtd_partitions);
int mtd_is_master(struct mtd_info *mtd)
{
struct mtd_part *part;
int nopart = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry(part, &mtd_partitions, list)
if (&part->mtd == mtd) {
nopart = 1;
break;
}
mutex_unlock(&mtd_partitions_mutex);
return nopart;
}
EXPORT_SYMBOL_GPL(mtd_is_master);
......@@ -400,13 +400,6 @@ config MTD_NAND_PXA3xx
This enables the driver for the NAND flash device found on
PXA3xx processors
config MTD_NAND_PXA3xx_BUILTIN
bool "Use builtin definitions for some NAND chips (deprecated)"
depends on MTD_NAND_PXA3xx
help
This enables builtin definitions for some NAND chips. This
is deprecated in favor of platform specific data.
config MTD_NAND_CM_X270
tristate "Support for NAND Flash on CM-X270 modules"
depends on MACH_ARMCORE
......@@ -458,6 +451,7 @@ config MTD_NAND_ORION
config MTD_NAND_FSL_ELBC
tristate "NAND support for Freescale eLBC controllers"
depends on PPC_OF
select FSL_LBC
help
Various Freescale chips, including the 8313, include a NAND Flash
Controller Module with built-in hardware ECC capabilities.
......@@ -531,4 +525,11 @@ config MTD_NAND_JZ4740
help
Enables support for NAND Flash on JZ4740 SoC based boards.
config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC"
depends on PLAT_SPEAR || PLAT_NOMADIK || MACH_U300
help
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
endif # MTD_NAND
......@@ -19,6 +19,7 @@ obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
......
......@@ -110,15 +110,6 @@ static const unsigned short bfin_nfc_pin_req[] =
0};
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
static uint8_t bbt_pattern[] = { 0xff };
static struct nand_bbt_descr bootrom_bbt = {
.options = 0,
.offs = 63,
.len = 1,
.pattern = bbt_pattern,
};
static struct nand_ecclayout bootrom_ecclayout = {
.eccbytes = 24,
.eccpos = {
......@@ -809,7 +800,6 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
/* setup hardware ECC data struct */
if (hardware_ecc) {
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->badblock_pattern = &bootrom_bbt;
chip->ecc.layout = &bootrom_ecclayout;
#endif
chip->read_buf = bf5xx_nand_dma_read_buf;
......@@ -830,6 +820,10 @@ static int __devinit bf5xx_nand_probe(struct platform_device *pdev)
goto out_err_nand_scan;
}
#ifdef CONFIG_MTD_NAND_BF5XX_BOOTROM_ECC
chip->badblockpos = 63;
#endif
/* add NAND partition */
bf5xx_nand_add_partition(info);
......
......@@ -316,7 +316,7 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
u32 syndrome[4];
u32 ecc_state;
unsigned num_errors, corrected;
unsigned long timeo = jiffies + msecs_to_jiffies(100);
unsigned long timeo;
/* All bytes 0xff? It's an erased page; ignore its ECC. */
for (i = 0; i < 10; i++) {
......@@ -372,9 +372,11 @@ static int nand_davinci_correct_4bit(struct mtd_info *mtd,
* after setting the 4BITECC_ADD_CALC_START bit. So if you immediately
* begin trying to poll for the state, you may fall right out of your
* loop without any of the correction calculations having taken place.
* The recommendation from the hardware team is to wait till ECC_STATE
* reads less than 4, which means ECC HW has entered correction state.
* The recommendation from the hardware team is to initially delay as
* long as ECC_STATE reads less than 4. After that, ECC HW has entered
* correction state.
*/
timeo = jiffies + usecs_to_jiffies(100);
do {
ecc_state = (davinci_nand_readl(info,
NANDFSR_OFFSET) >> 8) & 0x0f;
......@@ -733,6 +735,9 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
* breaks userspace ioctl interface with mtd-utils. Once we
* resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
* for the 4KiB page chips.
*
* TODO: Note that nand_ecclayout has now been expanded and can
* hold plenty of OOB entries.
*/
dev_warn(&pdev->dev, "no 4-bit ECC support yet "
"for 4KiB-page NAND\n");
......
......@@ -1292,6 +1292,7 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
read_status(denali);
break;
case NAND_CMD_READID:
case NAND_CMD_PARAM:
reset_buf(denali);
/*sometimes ManufactureId read from register is not right
* e.g. some of Micron MT29F32G08QAA MLC NAND chips
......
此差异已折叠。
......@@ -186,7 +186,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
if (!flash_np)
return -ENODEV;
fun->mtd.name = kasprintf(GFP_KERNEL, "%x.%s", io_res->start,
fun->mtd.name = kasprintf(GFP_KERNEL, "0x%llx.%s", (u64)io_res->start,
flash_np->name);
if (!fun->mtd.name) {
ret = -ENOMEM;
......@@ -222,7 +222,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
{
struct fsl_upm_nand *fun;
struct resource io_res;
const uint32_t *prop;
const __be32 *prop;
int rnb_gpio;
int ret;
int size;
......@@ -270,7 +270,7 @@ static int __devinit fun_probe(struct platform_device *ofdev,
goto err1;
}
for (i = 0; i < fun->mchip_count; i++)
fun->mchip_offsets[i] = prop[i];
fun->mchip_offsets[i] = be32_to_cpu(prop[i]);
} else {
fun->mchip_count = 1;
}
......@@ -295,13 +295,13 @@ static int __devinit fun_probe(struct platform_device *ofdev,
prop = of_get_property(ofdev->dev.of_node, "chip-delay", NULL);
if (prop)
fun->chip_delay = *prop;
fun->chip_delay = be32_to_cpup(prop);
else
fun->chip_delay = 50;
prop = of_get_property(ofdev->dev.of_node, "fsl,upm-wait-flags", &size);
if (prop && size == sizeof(uint32_t))
fun->wait_flags = *prop;
fun->wait_flags = be32_to_cpup(prop);
else
fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
FSL_UPM_WAIT_WRITE_BYTE;
......
此差异已折叠。
......@@ -568,6 +568,7 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
uint rcw_width;
uint rcwh;
uint romloc, ps;
int ret = 0;
rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
if (!rmnode) {
......@@ -579,7 +580,8 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
rm = of_iomap(rmnode, 0);
if (!rm) {
dev_err(prv->dev, "Error mapping reset module node!\n");
return -EBUSY;
ret = -EBUSY;
goto out;
}
rcwh = in_be32(&rm->rcwhr);
......@@ -628,8 +630,9 @@ static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
rcw_width * 8, rcw_pagesize,
rcw_sparesize);
iounmap(rm);
out:
of_node_put(rmnode);
return 0;
return ret;
}
/* Free driver resources */
......@@ -660,7 +663,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
#endif
struct nand_chip *chip;
unsigned long regs_paddr, regs_size;
const uint *chips_no;
const __be32 *chips_no;
int resettime = 0;
int retval = 0;
int rev, len;
......@@ -803,7 +806,7 @@ static int __devinit mpc5121_nfc_probe(struct platform_device *op,
}
/* Detect NAND chips */
if (nand_scan(mtd, *chips_no)) {
if (nand_scan(mtd, be32_to_cpup(chips_no))) {
dev_err(dev, "NAND Flash not found !\n");
devm_free_irq(dev, prv->irq, mtd);
retval = -ENXIO;
......
此差异已折叠。
......@@ -13,28 +13,37 @@
* Description:
*
* When nand_scan_bbt is called, then it tries to find the bad block table
* depending on the options in the bbt descriptor(s). If a bbt is found
* then the contents are read and the memory based bbt is created. If a
* mirrored bbt is selected then the mirror is searched too and the
* versions are compared. If the mirror has a greater version number
* than the mirror bbt is used to build the memory based bbt.
* depending on the options in the BBT descriptor(s). If no flash based BBT
* (NAND_USE_FLASH_BBT) is specified then the device is scanned for factory
* marked good / bad blocks. This information is used to create a memory BBT.
* Once a new bad block is discovered then the "factory" information is updated
* on the device.
* If a flash based BBT is specified then the function first tries to find the
* BBT on flash. If a BBT is found then the contents are read and the memory
* based BBT is created. If a mirrored BBT is selected then the mirror is
* searched too and the versions are compared. If the mirror has a greater
* version number than the mirror BBT is used to build the memory based BBT.
* If the tables are not versioned, then we "or" the bad block information.
* If one of the bbt's is out of date or does not exist it is (re)created.
* If no bbt exists at all then the device is scanned for factory marked
* If one of the BBTs is out of date or does not exist it is (re)created.
* If no BBT exists at all then the device is scanned for factory marked
* good / bad blocks and the bad block tables are created.
*
* For manufacturer created bbts like the one found on M-SYS DOC devices
* the bbt is searched and read but never created
* For manufacturer created BBTs like the one found on M-SYS DOC devices
* the BBT is searched and read but never created
*
* The autogenerated bad block table is located in the last good blocks
* The auto generated bad block table is located in the last good blocks
* of the device. The table is mirrored, so it can be updated eventually.
* The table is marked in the oob area with an ident pattern and a version
* number which indicates which of both tables is more up to date.
* The table is marked in the OOB area with an ident pattern and a version
* number which indicates which of both tables is more up to date. If the NAND
* controller needs the complete OOB area for the ECC information then the
* option NAND_USE_FLASH_BBT_NO_OOB should be used: it moves the ident pattern
* and the version byte into the data area and the OOB area will remain
* untouched.
*
* The table uses 2 bits per block
* 11b: block is good
* 00b: block is factory marked bad
* 01b, 10b: block is marked bad due to wear
* 11b: block is good
* 00b: block is factory marked bad
* 01b, 10b: block is marked bad due to wear
*
* The memory bad block table uses the following scheme:
* 00b: block is good
......@@ -59,6 +68,16 @@
#include <linux/delay.h>
#include <linux/vmalloc.h>
static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td)
{
int ret;
ret = memcmp(buf, td->pattern, td->len);
if (!ret)
return ret;
return -1;
}
/**
* check_pattern - [GENERIC] check if a pattern is in the buffer
* @buf: the buffer to search
......@@ -77,6 +96,9 @@ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_desc
int i, end = 0;
uint8_t *p = buf;
if (td->options & NAND_BBT_NO_OOB)
return check_pattern_no_oob(buf, td);
end = paglen + td->offs;
if (td->options & NAND_BBT_SCANEMPTY) {
for (i = 0; i < end; i++) {
......@@ -155,33 +177,64 @@ static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
return 0;
}
/**
* add_marker_len - compute the length of the marker in data area
* @td: BBT descriptor used for computation
*
* The length will be 0 if the markeris located in OOB area.
*/
static u32 add_marker_len(struct nand_bbt_descr *td)
{
u32 len;
if (!(td->options & NAND_BBT_NO_OOB))
return 0;
len = td->len;
if (td->options & NAND_BBT_VERSION)
len++;
return len;
}
/**
* read_bbt - [GENERIC] Read the bad block table starting from page
* @mtd: MTD device structure
* @buf: temporary buffer
* @page: the starting page
* @num: the number of bbt descriptors to read
* @bits: number of bits per block
* @td: the bbt describtion table
* @offs: offset in the memory table
* @reserved_block_code: Pattern to identify reserved blocks
*
* Read the bad block table starting from page.
*
*/
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
int bits, int offs, int reserved_block_code)
struct nand_bbt_descr *td, int offs)
{
int res, i, j, act = 0;
struct nand_chip *this = mtd->priv;
size_t retlen, len, totlen;
loff_t from;
int bits = td->options & NAND_BBT_NRBITS_MSK;
uint8_t msk = (uint8_t) ((1 << bits) - 1);
u32 marker_len;
int reserved_block_code = td->reserved_block_code;
totlen = (num * bits) >> 3;
marker_len = add_marker_len(td);
from = ((loff_t) page) << this->page_shift;
while (totlen) {
len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
if (marker_len) {
/*
* In case the BBT marker is not in the OOB area it
* will be just in the first page.
*/
len -= marker_len;
from += marker_len;
marker_len = 0;
}
res = mtd->read(mtd, from, len, &retlen, buf);
if (res < 0) {
if (retlen != len) {
......@@ -238,30 +291,47 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
{
struct nand_chip *this = mtd->priv;
int res = 0, i;
int bits;
bits = td->options & NAND_BBT_NRBITS_MSK;
if (td->options & NAND_BBT_PERCHIP) {
int offs = 0;
for (i = 0; i < this->numchips; i++) {
if (chip == -1 || chip == i)
res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
res = read_bbt(mtd, buf, td->pages[i],
this->chipsize >> this->bbt_erase_shift,
td, offs);
if (res)
return res;
offs += this->chipsize >> (this->bbt_erase_shift + 2);
}
} else {
res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
res = read_bbt(mtd, buf, td->pages[0],
mtd->size >> this->bbt_erase_shift, td, 0);
if (res)
return res;
}
return 0;
}
/*
* BBT marker is in the first page, no OOB.
*/
static int scan_read_raw_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
struct nand_bbt_descr *td)
{
size_t retlen;
size_t len;
len = td->len;
if (td->options & NAND_BBT_VERSION)
len++;
return mtd->read(mtd, offs, len, &retlen, buf);
}
/*
* Scan read raw data from flash
*/
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
static int scan_read_raw_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len)
{
struct mtd_oob_ops ops;
......@@ -294,6 +364,15 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
return 0;
}
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len, struct nand_bbt_descr *td)
{
if (td->options & NAND_BBT_NO_OOB)
return scan_read_raw_data(mtd, buf, offs, td);
else
return scan_read_raw_oob(mtd, buf, offs, len);
}
/*
* Scan write data with oob to flash
*/
......@@ -312,6 +391,15 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
return mtd->write_oob(mtd, offs, &ops);
}
static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
{
u32 ver_offs = td->veroffs;
if (!(td->options & NAND_BBT_NO_OOB))
ver_offs += mtd->writesize;
return ver_offs;
}
/**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
* @mtd: MTD device structure
......@@ -331,8 +419,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
mtd->writesize);
td->version[0] = buf[mtd->writesize + td->veroffs];
mtd->writesize, td);
td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
td->pages[0], td->version[0]);
}
......@@ -340,8 +428,8 @@ static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
/* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) {
scan_read_raw(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
mtd->writesize);
md->version[0] = buf[mtd->writesize + md->veroffs];
mtd->writesize, td);
md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]);
}
......@@ -357,7 +445,7 @@ static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
{
int ret, j;
ret = scan_read_raw(mtd, buf, offs, readlen);
ret = scan_read_raw_oob(mtd, buf, offs, readlen);
if (ret)
return ret;
......@@ -464,6 +552,8 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
for (i = startblock; i < numblocks;) {
int ret;
BUG_ON(bd->options & NAND_BBT_NO_OOB);
if (bd->options & NAND_BBT_SCANALLPAGES)
ret = scan_block_full(mtd, bd, from, buf, readlen,
scanlen, len);
......@@ -545,11 +635,12 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
/* Read first page */
scan_read_raw(mtd, buf, offs, mtd->writesize);
scan_read_raw(mtd, buf, offs, mtd->writesize, td);
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) {
td->version[i] = buf[mtd->writesize + td->veroffs];
offs = bbt_get_ver_offs(mtd, td);
td->version[i] = buf[offs];
}
break;
}
......@@ -733,12 +824,26 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
ooboffs = len + (pageoffs * mtd->oobsize);
} else if (td->options & NAND_BBT_NO_OOB) {
ooboffs = 0;
offs = td->len;
/* the version byte */
if (td->options & NAND_BBT_VERSION)
offs++;
/* Calc length */
len = (size_t) (numblocks >> sft);
len += offs;
/* Make it page aligned ! */
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len);
/* Pattern is located at the begin of first page */
memcpy(buf, td->pattern, td->len);
} else {
/* Calc length */
len = (size_t) (numblocks >> sft);
/* Make it page aligned ! */
len = (len + (mtd->writesize - 1)) &
~(mtd->writesize - 1);
len = ALIGN(len, mtd->writesize);
/* Preset the buffer with 0xff */
memset(buf, 0xff, len +
(len >> this->page_shift)* mtd->oobsize);
......@@ -772,7 +877,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
if (res < 0)
goto outerr;
res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
res = scan_write_bbt(mtd, to, len, buf,
td->options & NAND_BBT_NO_OOB ? NULL :
&buf[len]);
if (res < 0)
goto outerr;
......@@ -892,7 +999,8 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
continue;
/* Create the table in memory by scanning the chip(s) */
create_bbt(mtd, buf, bd, chipsel);
if (!(this->options & NAND_CREATE_EMPTY_BBT))
create_bbt(mtd, buf, bd, chipsel);
td->version[i] = 1;
if (md)
......@@ -982,6 +1090,49 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
}
}
/**
* verify_bbt_descr - verify the bad block description
* @bd: the table to verify
*
* This functions performs a few sanity checks on the bad block description
* table.
*/
static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
u32 pattern_len = bd->len;
u32 bits = bd->options & NAND_BBT_NRBITS_MSK;
u32 table_size;
if (!bd)
return;
BUG_ON((this->options & NAND_USE_FLASH_BBT_NO_OOB) &&
!(this->options & NAND_USE_FLASH_BBT));
BUG_ON(!bits);
if (bd->options & NAND_BBT_VERSION)
pattern_len++;
if (bd->options & NAND_BBT_NO_OOB) {
BUG_ON(!(this->options & NAND_USE_FLASH_BBT));
BUG_ON(!(this->options & NAND_USE_FLASH_BBT_NO_OOB));
BUG_ON(bd->offs);
if (bd->options & NAND_BBT_VERSION)
BUG_ON(bd->veroffs != bd->len);
BUG_ON(bd->options & NAND_BBT_SAVECONTENT);
}
if (bd->options & NAND_BBT_PERCHIP)
table_size = this->chipsize >> this->bbt_erase_shift;
else
table_size = mtd->size >> this->bbt_erase_shift;
table_size >>= 3;
table_size *= bits;
if (bd->options & NAND_BBT_NO_OOB)
table_size += pattern_len;
BUG_ON(table_size > (1 << this->bbt_erase_shift));
}
/**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
* @mtd: MTD device structure
......@@ -1023,6 +1174,8 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
}
return res;
}
verify_bbt_descr(mtd, td);
verify_bbt_descr(mtd, md);
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
......@@ -1166,6 +1319,26 @@ static struct nand_bbt_descr bbt_mirror_descr = {
.pattern = mirror_pattern
};
static struct nand_bbt_descr bbt_main_no_bbt_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB,
.len = 4,
.veroffs = 4,
.maxblocks = 4,
.pattern = bbt_pattern
};
static struct nand_bbt_descr bbt_mirror_no_bbt_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP
| NAND_BBT_NO_OOB,
.len = 4,
.veroffs = 4,
.maxblocks = 4,
.pattern = mirror_pattern
};
#define BBT_SCAN_OPTIONS (NAND_BBT_SCANLASTPAGE | NAND_BBT_SCAN2NDPAGE | \
NAND_BBT_SCANBYTE1AND6)
/**
......@@ -1236,8 +1409,13 @@ int nand_default_bbt(struct mtd_info *mtd)
if (this->options & NAND_USE_FLASH_BBT) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
if (this->options & NAND_USE_FLASH_BBT_NO_OOB) {
this->bbt_td = &bbt_main_no_bbt_descr;
this->bbt_md = &bbt_mirror_no_bbt_descr;
} else {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
}
if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
......
......@@ -75,9 +75,13 @@ struct nand_flash_dev nand_flash_ids[] = {
/*512 Megabit */
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16},
/* 1 Gigabit */
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
......@@ -112,7 +116,34 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
/* 32 Gigabit */
{"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS},
{"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
{"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16},
{"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16},
/* 64 Gigabit */
{"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS},
{"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
{"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16},
{"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16},
/* 128 Gigabit */
{"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS},
{"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
{"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16},
{"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16},
/* 256 Gigabit */
{"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS},
{"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
{"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16},
{"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16},
/* 512 Gigabit */
{"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS},
{"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS},
{"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16},
{"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16},
/*
* Renesas AND 1 Gigabit. Those chips do not support extended id and
......
......@@ -107,6 +107,7 @@ static char *gravepages = NULL;
static unsigned int rptwear = 0;
static unsigned int overridesize = 0;
static char *cache_file = NULL;
static unsigned int bbt;
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
......@@ -130,6 +131,7 @@ module_param(gravepages, charp, 0400);
module_param(rptwear, uint, 0400);
module_param(overridesize, uint, 0400);
module_param(cache_file, charp, 0400);
module_param(bbt, uint, 0400);
MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
......@@ -162,6 +164,7 @@ MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the I
"The size is specified in erase blocks and as the exponent of a power of two"
" e.g. 5 means a size of 32 erase blocks");
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 BBT with marker in data area");
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 4096
......@@ -2264,6 +2267,18 @@ static int __init ns_init_module(void)
/* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN;
switch (bbt) {
case 2:
chip->options |= NAND_USE_FLASH_BBT_NO_OOB;
case 1:
chip->options |= NAND_USE_FLASH_BBT;
case 0:
break;
default:
NS_ERR("bbt has to be 0..2\n");
retval = -EINVAL;
goto error;
}
/*
* Perform minimum nandsim structure initialization to handle
* the initial ID read command correctly
......@@ -2321,10 +2336,10 @@ static int __init ns_init_module(void)
if ((retval = init_nandsim(nsmtd)) != 0)
goto err_exit;
if ((retval = parse_badblocks(nand, nsmtd)) != 0)
if ((retval = nand_default_bbt(nsmtd)) != 0)
goto err_exit;
if ((retval = nand_default_bbt(nsmtd)) != 0)
if ((retval = parse_badblocks(nand, nsmtd)) != 0)
goto err_exit;
/* Register NAND partitions */
......
此差异已折叠。
......@@ -111,11 +111,11 @@ static int use_dma = 1;
module_param(use_dma, bool, 0);
MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
#else
const int use_dma;
static const int use_dma;
#endif
#else
const int use_prefetch;
const int use_dma;
static const int use_dma;
#endif
struct omap_nand_info {
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册