Commit 22ae77bc authored by Linus Torvalds's avatar Linus Torvalds

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

* git://git.infradead.org/mtd-2.6: (53 commits)
  [MTD] struct device - replace bus_id with dev_name(), dev_set_name()
  [MTD] [NOR] Fixup for Numonyx M29W128 chips
  [MTD] mtdpart: Make ecc_stats more realistic.
  powerpc/85xx: TQM8548: Update DTS file for multi-chip support
  powerpc: NAND: FSL UPM: document new bindings
  [MTD] [NAND] FSL-UPM: Add wait flags to support board/chip specific delays
  [MTD] [NAND] FSL-UPM: add multi chip support
  [MTD] [NOR] Add device parent info to physmap_of
  [MTD] [NAND] Add support for NAND on the Socrates board
  [MTD] [NAND] Add support for 4KiB pages.
  [MTD] sysfs support should not depend on CONFIG_PROC_FS
  [MTD] [NAND] Add parent info for CAFÉ controller
  [MTD] support driver model updates
  [MTD] driver model updates (part 2)
  [MTD] driver model updates
  [MTD] [NAND] move gen_nand's probe function to .devinit.text
  [MTD] [MAPS] move sa1100 flash's probe function to .devinit.text
  [MTD] fix use after free in register_mtd_blktrans
  [MTD] [MAPS] Drop now unused sharpsl-flash map
  [MTD] ofpart: Check name property to determine partition nodes.
  ...

Manually fix trivial conflict in drivers/mtd/maps/Makefile
parents e379ec7c 30bbf140
......@@ -5,9 +5,21 @@ Required properties:
- reg : should specify localbus chip select and size used for the chip.
- fsl,upm-addr-offset : UPM pattern offset for the address latch.
- fsl,upm-cmd-offset : UPM pattern offset for the command latch.
- gpios : may specify optional GPIO connected to the Ready-Not-Busy pin.
Example:
Optional properties:
- fsl,upm-wait-flags : add chip-dependent short delays after running the
UPM pattern (0x1), after writing a data byte (0x2) or after
writing out a buffer (0x4).
- fsl,upm-addr-line-cs-offsets : address offsets for multi-chip support.
The corresponding address lines are used to select the chip.
- gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins
(R/B#). For multi-chip devices, "n" GPIO definitions are required
according to the number of chips.
- chip-delay : chip dependent delay for transfering data from array to
read registers (tR). Required if property "gpios" is not used
(R/B# pins not connected).
Examples:
upm@1,0 {
compatible = "fsl,upm-nand";
......@@ -26,3 +38,26 @@ upm@1,0 {
};
};
};
upm@3,0 {
#address-cells = <0>;
#size-cells = <0>;
compatible = "tqc,tqm8548-upm-nand", "fsl,upm-nand";
reg = <3 0x0 0x800>;
fsl,upm-addr-offset = <0x10>;
fsl,upm-cmd-offset = <0x08>;
/* Multi-chip NAND device */
fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
fsl,upm-wait-flags = <0x5>;
chip-delay = <25>; // in micro-seconds
nand@0 {
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "fs";
reg = <0x00000000 0x10000000>;
};
};
};
/*
* mach-davinci/nand.h
*
* Copyright © 2006 Texas Instruments.
*
* Ported to 2.6.23 Copyright © 2008 by
* Sander Huijsen <Shuijsen@optelecom-nkf.com>
* Troy Kisky <troy.kisky@boundarydevices.com>
* Dirk Behme <Dirk.Behme@gmail.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
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ARCH_ARM_DAVINCI_NAND_H
#define __ARCH_ARM_DAVINCI_NAND_H
#include <linux/mtd/nand.h>
#define NRCSR_OFFSET 0x00
#define AWCCR_OFFSET 0x04
#define A1CR_OFFSET 0x10
#define NANDFCR_OFFSET 0x60
#define NANDFSR_OFFSET 0x64
#define NANDF1ECC_OFFSET 0x70
/* 4-bit ECC syndrome registers */
#define NAND_4BIT_ECC_LOAD_OFFSET 0xbc
#define NAND_4BIT_ECC1_OFFSET 0xc0
#define NAND_4BIT_ECC2_OFFSET 0xc4
#define NAND_4BIT_ECC3_OFFSET 0xc8
#define NAND_4BIT_ECC4_OFFSET 0xcc
#define NAND_ERR_ADD1_OFFSET 0xd0
#define NAND_ERR_ADD2_OFFSET 0xd4
#define NAND_ERR_ERRVAL1_OFFSET 0xd8
#define NAND_ERR_ERRVAL2_OFFSET 0xdc
/* NOTE: boards don't need to use these address bits
* for ALE/CLE unless they support booting from NAND.
* They're used unless platform data overrides them.
*/
#define MASK_ALE 0x08
#define MASK_CLE 0x10
struct davinci_nand_pdata { /* platform_data */
uint32_t mask_ale;
uint32_t mask_cle;
/* for packages using two chipselects */
uint32_t mask_chipsel;
/* board's default static partition info */
struct mtd_partition *parts;
unsigned nr_parts;
/* none == NAND_ECC_NONE (strongly *not* advised!!)
* soft == NAND_ECC_SOFT
* 1-bit == NAND_ECC_HW
* 4-bit == NAND_ECC_HW_SYNDROME (not on all chips)
*/
nand_ecc_modes_t ecc_mode;
/* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
unsigned options;
};
#endif /* __ARCH_ARM_DAVINCI_NAND_H */
......@@ -49,6 +49,9 @@ struct pxa3xx_nand_platform_data {
*/
int enable_arbiter;
/* allow platform code to keep OBM/bootloader defined NFC config */
int keep_config;
const struct mtd_partition *parts;
unsigned int nr_parts;
......
......@@ -337,7 +337,7 @@ int _access_ok(unsigned long addr, unsigned long size)
if (addr >= memory_mtd_end && (addr + size) <= physical_mem_end)
return 1;
#ifdef CONFIG_ROMFS_MTD_FS
#ifdef CONFIG_ROMFS_ON_MTD
/* For XIP, allow user space to use pointers within the ROMFS. */
if (addr >= memory_mtd_start && (addr + size) <= memory_mtd_end)
return 1;
......
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* (C) Copyright TOSHIBA CORPORATION 2007
*/
#ifndef __ASM_TXX9_NDFMC_H
#define __ASM_TXX9_NDFMC_H
#define NDFMC_PLAT_FLAG_USE_BSPRT 0x01
#define NDFMC_PLAT_FLAG_NO_RSTR 0x02
#define NDFMC_PLAT_FLAG_HOLDADD 0x04
#define NDFMC_PLAT_FLAG_DUMMYWRITE 0x08
struct txx9ndfmc_platform_data {
unsigned int shift;
unsigned int gbus_clock;
unsigned int hold; /* hold time in nanosecond */
unsigned int spw; /* strobe pulse width in nanosecond */
unsigned int flags;
unsigned char ch_mask; /* available channel bitmask */
unsigned char wp_mask; /* write-protect bitmask */
unsigned char wide_mask; /* 16bit-nand bitmask */
};
void txx9_ndfmc_init(unsigned long baseaddr,
const struct txx9ndfmc_platform_data *plat_data);
#endif /* __ASM_TXX9_NDFMC_H */
......@@ -130,4 +130,13 @@
void rbtx4939_prom_init(void);
void rbtx4939_irq_setup(void);
struct mtd_partition;
struct map_info;
struct rbtx4939_flash_data {
unsigned int width;
unsigned int nr_parts;
struct mtd_partition *parts;
void (*map_init)(struct map_info *map);
};
#endif /* __ASM_TXX9_RBTX4939_H */
......@@ -291,6 +291,7 @@ int tx4938_pcic1_map_irq(const struct pci_dev *dev, u8 slot);
void tx4938_setup_pcierr_irq(void);
void tx4938_irq_init(void);
void tx4938_mtd_init(int ch);
void tx4938_ndfmc_init(unsigned int hold, unsigned int spw);
struct tx4938ide_platform_info {
/*
......
......@@ -542,5 +542,7 @@ int tx4939_irq(void);
void tx4939_mtd_init(int ch);
void tx4939_ata_init(void);
void tx4939_rtc_init(void);
void tx4939_ndfmc_init(unsigned int hold, unsigned int spw,
unsigned char ch_mask, unsigned char wide_mask);
#endif /* __ASM_TXX9_TX4939_H */
......@@ -32,6 +32,7 @@
#include <asm/txx9/generic.h>
#include <asm/txx9/pci.h>
#include <asm/txx9tmr.h>
#include <asm/txx9/ndfmc.h>
#ifdef CONFIG_CPU_TX49XX
#include <asm/txx9/tx4938.h>
#endif
......@@ -691,6 +692,26 @@ void __init txx9_physmap_flash_init(int no, unsigned long addr,
#endif
}
void __init txx9_ndfmc_init(unsigned long baseaddr,
const struct txx9ndfmc_platform_data *pdata)
{
#if defined(CONFIG_MTD_NAND_TXX9NDFMC) || \
defined(CONFIG_MTD_NAND_TXX9NDFMC_MODULE)
struct resource res = {
.start = baseaddr,
.end = baseaddr + 0x1000 - 1,
.flags = IORESOURCE_MEM,
};
struct platform_device *pdev = platform_device_alloc("txx9ndfmc", -1);
if (!pdev ||
platform_device_add_resources(pdev, &res, 1) ||
platform_device_add_data(pdev, pdata, sizeof(*pdata)) ||
platform_device_add(pdev))
platform_device_put(pdev);
#endif
}
#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
static DEFINE_SPINLOCK(txx9_iocled_lock);
......
......@@ -23,6 +23,7 @@
#include <asm/txx9tmr.h>
#include <asm/txx9pio.h>
#include <asm/txx9/generic.h>
#include <asm/txx9/ndfmc.h>
#include <asm/txx9/tx4938.h>
static void __init tx4938_wdr_init(void)
......@@ -382,6 +383,26 @@ void __init tx4938_ata_init(unsigned int irq, unsigned int shift, int tune)
platform_device_put(pdev);
}
void __init tx4938_ndfmc_init(unsigned int hold, unsigned int spw)
{
struct txx9ndfmc_platform_data plat_data = {
.shift = 1,
.gbus_clock = txx9_gbus_clock,
.hold = hold,
.spw = spw,
.ch_mask = 1,
};
unsigned long baseaddr = TX4938_NDFMC_REG & 0xfffffffffULL;
#ifdef __BIG_ENDIAN
baseaddr += 4;
#endif
if ((__raw_readq(&tx4938_ccfgptr->pcfg) &
(TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) ==
TX4938_PCFG_NDF_SEL)
txx9_ndfmc_init(baseaddr, &plat_data);
}
static void __init tx4938_stop_unused_modules(void)
{
__u64 pcfg, rst = 0, ckd = 0;
......
......@@ -27,6 +27,7 @@
#include <asm/txx9irq.h>
#include <asm/txx9tmr.h>
#include <asm/txx9/generic.h>
#include <asm/txx9/ndfmc.h>
#include <asm/txx9/tx4939.h>
static void __init tx4939_wdr_init(void)
......@@ -457,6 +458,22 @@ void __init tx4939_rtc_init(void)
platform_device_register(&rtc_dev);
}
void __init tx4939_ndfmc_init(unsigned int hold, unsigned int spw,
unsigned char ch_mask, unsigned char wide_mask)
{
struct txx9ndfmc_platform_data plat_data = {
.shift = 1,
.gbus_clock = txx9_gbus_clock,
.hold = hold,
.spw = spw,
.flags = NDFMC_PLAT_FLAG_NO_RSTR | NDFMC_PLAT_FLAG_HOLDADD |
NDFMC_PLAT_FLAG_DUMMYWRITE,
.ch_mask = ch_mask,
.wide_mask = wide_mask,
};
txx9_ndfmc_init(TX4939_NDFMC_REG & 0xfffffffffULL, &plat_data);
}
static void __init tx4939_stop_unused_modules(void)
{
__u64 pcfg, rst = 0, ckd = 0;
......
......@@ -352,6 +352,8 @@ static void __init rbtx4938_device_init(void)
rbtx4938_ne_init();
tx4938_wdt_init();
rbtx4938_mtd_init();
/* TC58DVM82A1FT: tDH=10ns, tWP=tRP=tREADID=35ns */
tx4938_ndfmc_init(10, 35);
tx4938_ata_init(RBTX4938_IRQ_IOC_ATA, 0, 1);
txx9_iocled_init(RBTX4938_LED_ADDR - IO_BASE, -1, 8, 1, "green", NULL);
}
......
......@@ -16,6 +16,9 @@
#include <linux/leds.h>
#include <linux/interrupt.h>
#include <linux/smc91x.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/map.h>
#include <asm/reboot.h>
#include <asm/txx9/generic.h>
#include <asm/txx9/pci.h>
......@@ -282,6 +285,159 @@ static void rbtx4939_7segled_putc(unsigned int pos, unsigned char val)
__rbtx4939_7segled_putc(pos, val);
}
#if defined(CONFIG_MTD_RBTX4939) || defined(CONFIG_MTD_RBTX4939_MODULE)
/* special mapping for boot rom */
static unsigned long rbtx4939_flash_fixup_ofs(unsigned long ofs)
{
u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
unsigned char shift;
if (bdipsw & 8) {
/* BOOT Mode: USER ROM1 / USER ROM2 */
shift = bdipsw & 3;
/* rotate A[23:22] */
return (ofs & ~0xc00000) | ((((ofs >> 22) + shift) & 3) << 22);
}
#ifdef __BIG_ENDIAN
if (bdipsw == 0)
/* BOOT Mode: Monitor ROM */
ofs ^= 0x400000; /* swap A[22] */
#endif
return ofs;
}
static map_word rbtx4939_flash_read16(struct map_info *map, unsigned long ofs)
{
map_word r;
ofs = rbtx4939_flash_fixup_ofs(ofs);
r.x[0] = __raw_readw(map->virt + ofs);
return r;
}
static void rbtx4939_flash_write16(struct map_info *map, const map_word datum,
unsigned long ofs)
{
ofs = rbtx4939_flash_fixup_ofs(ofs);
__raw_writew(datum.x[0], map->virt + ofs);
mb(); /* see inline_map_write() in mtd/map.h */
}
static void rbtx4939_flash_copy_from(struct map_info *map, void *to,
unsigned long from, ssize_t len)
{
u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
unsigned char shift;
ssize_t curlen;
from += (unsigned long)map->virt;
if (bdipsw & 8) {
/* BOOT Mode: USER ROM1 / USER ROM2 */
shift = bdipsw & 3;
while (len) {
curlen = min_t(unsigned long, len,
0x400000 - (from & (0x400000 - 1)));
memcpy(to,
(void *)((from & ~0xc00000) |
((((from >> 22) + shift) & 3) << 22)),
curlen);
len -= curlen;
from += curlen;
to += curlen;
}
return;
}
#ifdef __BIG_ENDIAN
if (bdipsw == 0) {
/* BOOT Mode: Monitor ROM */
while (len) {
curlen = min_t(unsigned long, len,
0x400000 - (from & (0x400000 - 1)));
memcpy(to, (void *)(from ^ 0x400000), curlen);
len -= curlen;
from += curlen;
to += curlen;
}
return;
}
#endif
memcpy(to, (void *)from, len);
}
static void rbtx4939_flash_map_init(struct map_info *map)
{
map->read = rbtx4939_flash_read16;
map->write = rbtx4939_flash_write16;
map->copy_from = rbtx4939_flash_copy_from;
}
static void __init rbtx4939_mtd_init(void)
{
static struct {
struct platform_device dev;
struct resource res;
struct rbtx4939_flash_data data;
} pdevs[4];
int i;
static char names[4][8];
static struct mtd_partition parts[4];
struct rbtx4939_flash_data *boot_pdata = &pdevs[0].data;
u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
if (bdipsw & 8) {
/* BOOT Mode: USER ROM1 / USER ROM2 */
boot_pdata->nr_parts = 4;
for (i = 0; i < boot_pdata->nr_parts; i++) {
sprintf(names[i], "img%d", 4 - i);
parts[i].name = names[i];
parts[i].size = 0x400000;
parts[i].offset = MTDPART_OFS_NXTBLK;
}
} else if (bdipsw == 0) {
/* BOOT Mode: Monitor ROM */
boot_pdata->nr_parts = 2;
strcpy(names[0], "big");
strcpy(names[1], "little");
for (i = 0; i < boot_pdata->nr_parts; i++) {
parts[i].name = names[i];
parts[i].size = 0x400000;
parts[i].offset = MTDPART_OFS_NXTBLK;
}
} else {
/* BOOT Mode: ROM Emulator */
boot_pdata->nr_parts = 2;
parts[0].name = "boot";
parts[0].offset = 0xc00000;
parts[0].size = 0x400000;
parts[1].name = "user";
parts[1].offset = 0;
parts[1].size = 0xc00000;
}
boot_pdata->parts = parts;
boot_pdata->map_init = rbtx4939_flash_map_init;
for (i = 0; i < ARRAY_SIZE(pdevs); i++) {
struct resource *r = &pdevs[i].res;
struct platform_device *dev = &pdevs[i].dev;
r->start = 0x1f000000 - i * 0x1000000;
r->end = r->start + 0x1000000 - 1;
r->flags = IORESOURCE_MEM;
pdevs[i].data.width = 2;
dev->num_resources = 1;
dev->resource = r;
dev->id = i;
dev->name = "rbtx4939-flash";
dev->dev.platform_data = &pdevs[i].data;
platform_device_register(dev);
}
}
#else
static void __init rbtx4939_mtd_init(void)
{
}
#endif
static void __init rbtx4939_arch_init(void)
{
rbtx4939_pci_setup();
......@@ -333,6 +489,11 @@ static void __init rbtx4939_device_init(void)
platform_device_add_data(pdev, &smc_pdata, sizeof(smc_pdata)) ||
platform_device_add(pdev))
platform_device_put(pdev);
rbtx4939_mtd_init();
/* TC58DVM82A1FT: tDH=10ns, tWP=tRP=tREADID=35ns */
tx4939_ndfmc_init(10, 35,
(1 << 1) | (1 << 2),
(1 << 2)); /* ch1:8bit, ch2:16bit */
rbtx4939_led_setup();
tx4939_wdt_init();
tx4939_ata_init();
......
......@@ -397,10 +397,13 @@ can1@2,100 {
upm@3,0 {
#address-cells = <0>;
#size-cells = <0>;
compatible = "fsl,upm-nand";
compatible = "tqc,tqm8548-upm-nand", "fsl,upm-nand";
reg = <3 0x0 0x800>;
fsl,upm-addr-offset = <0x10>;
fsl,upm-cmd-offset = <0x08>;
/* Micron MT29F8G08FAB multi-chip device */
fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
fsl,upm-wait-flags = <0x5>;
chip-delay = <25>; // in micro-seconds
nand@0 {
......@@ -409,7 +412,7 @@ nand@0 {
partition@0 {
label = "fs";
reg = <0x00000000 0x01000000>;
reg = <0x00000000 0x10000000>;
};
};
};
......
......@@ -397,10 +397,13 @@ can1@2,100 {
upm@3,0 {
#address-cells = <0>;
#size-cells = <0>;
compatible = "fsl,upm-nand";
compatible = "tqc,tqm8548-upm-nand", "fsl,upm-nand";
reg = <3 0x0 0x800>;
fsl,upm-addr-offset = <0x10>;
fsl,upm-cmd-offset = <0x08>;
/* Micron MT29F8G08FAB multi-chip device */
fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
fsl,upm-wait-flags = <0x5>;
chip-delay = <25>; // in micro-seconds
nand@0 {
......@@ -409,7 +412,7 @@ nand@0 {
partition@0 {
label = "fs";
reg = <0x00000000 0x01000000>;
reg = <0x00000000 0x10000000>;
};
};
};
......
......@@ -150,7 +150,7 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
spin_lock_irqsave(&fsl_lbc_lock, flags);
out_be32(&fsl_lbc_regs->mar, mar << (32 - upm->width));
out_be32(&fsl_lbc_regs->mar, mar);
switch (upm->width) {
case 8:
......
......@@ -4,7 +4,7 @@
# Core functionality.
obj-$(CONFIG_MTD) += mtd.o
mtd-y := mtdcore.o mtdsuper.o
mtd-y := mtdcore.o mtdsuper.o mtdbdi.o
mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
......
......@@ -44,8 +44,6 @@ struct ar7_bin_rec {
unsigned int address;
};
static struct mtd_partition ar7_parts[AR7_PARTS];
static int create_mtd_partitions(struct mtd_info *master,
struct mtd_partition **pparts,
unsigned long origin)
......@@ -57,7 +55,11 @@ static int create_mtd_partitions(struct mtd_info *master,
unsigned int root_offset = ROOT_OFFSET;
int retries = 10;
struct mtd_partition *ar7_parts;
ar7_parts = kzalloc(sizeof(*ar7_parts) * AR7_PARTS, GFP_KERNEL);
if (!ar7_parts)
return -ENOMEM;
ar7_parts[0].name = "loader";
ar7_parts[0].offset = 0;
ar7_parts[0].size = master->erasesize;
......
......@@ -1236,10 +1236,14 @@ static int inval_cache_and_wait_for_operation(
remove_wait_queue(&chip->wq, &wait);
spin_lock(chip->mutex);
}
if (chip->erase_suspended || chip->write_suspended) {
/* Suspend has occured while sleep: reset timeout */
if (chip->erase_suspended && chip_state == FL_ERASING) {
/* Erase suspend occured while sleep: reset timeout */
timeo = reset_timeo;
chip->erase_suspended = 0;
}
if (chip->write_suspended && chip_state == FL_WRITING) {
/* Write suspend occured while sleep: reset timeout */
timeo = reset_timeo;
chip->write_suspended = 0;
}
}
......
......@@ -282,6 +282,16 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
}
}
static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
if (cfi->cfiq->BufWriteTimeoutTyp) {
pr_warning("Don't use write buffer on ST flash M29W128G\n");
cfi->cfiq->BufWriteTimeoutTyp = 0;
}
}
static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
#ifdef AMD_BOOTLOC_BUG
......@@ -298,6 +308,7 @@ 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_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, },
#if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif
......
......@@ -159,6 +159,7 @@
#define SST39LF800 0x2781
#define SST39LF160 0x2782
#define SST39VF1601 0x234b
#define SST39VF3201 0x235b
#define SST39LF512 0x00D4
#define SST39LF010 0x00D5
#define SST39LF020 0x00D6
......@@ -1489,6 +1490,21 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x1000,256),
ERASEINFO(0x1000,256)
}
}, {
.mfr_id = MANUFACTURER_SST, /* should be CFI */
.dev_id = SST39VF3201,
.name = "SST 39VF3201",
.devtypes = CFI_DEVICETYPE_X16,
.uaddr = MTD_UADDR_0xAAAA_0x5555,
.dev_size = SIZE_4MiB,
.cmd_set = P_ID_AMD_STD,
.nr_regions = 4,
.regions = {
ERASEINFO(0x1000,256),
ERASEINFO(0x1000,256),
ERASEINFO(0x1000,256),
ERASEINFO(0x1000,256)
}
}, {
.mfr_id = MANUFACTURER_SST,
.dev_id = SST36VF3203,
......
......@@ -21,6 +21,8 @@ static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch
static int mapram_erase (struct mtd_info *, struct erase_info *);
static void mapram_nop (struct mtd_info *);
static struct mtd_info *map_ram_probe(struct map_info *map);
static unsigned long mapram_unmapped_area(struct mtd_info *, unsigned long,
unsigned long, unsigned long);
static struct mtd_chip_driver mapram_chipdrv = {
......@@ -64,6 +66,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
mtd->type = MTD_RAM;
mtd->size = map->size;
mtd->erase = mapram_erase;
mtd->get_unmapped_area = mapram_unmapped_area;
mtd->read = mapram_read;
mtd->write = mapram_write;
mtd->sync = mapram_nop;
......@@ -79,6 +82,20 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
}
/*
* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
static unsigned long mapram_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct map_info *map = mtd->priv;
return (unsigned long) map->virt + offset;
}
static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
......
......@@ -20,6 +20,8 @@ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch
static void maprom_nop (struct mtd_info *);
static struct mtd_info *map_rom_probe(struct map_info *map);
static int maprom_erase (struct mtd_info *mtd, struct erase_info *info);
static unsigned long maprom_unmapped_area(struct mtd_info *, unsigned long,
unsigned long, unsigned long);
static struct mtd_chip_driver maprom_chipdrv = {
.probe = map_rom_probe,
......@@ -40,6 +42,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
mtd->name = map->name;
mtd->type = MTD_ROM;
mtd->size = map->size;
mtd->get_unmapped_area = maprom_unmapped_area;
mtd->read = maprom_read;
mtd->write = maprom_write;
mtd->sync = maprom_nop;
......@@ -53,6 +56,20 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
}
/*
* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
static unsigned long maprom_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct map_info *map = mtd->priv;
return (unsigned long) map->virt + offset;
}
static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
struct map_info *map = mtd->priv;
......
......@@ -335,7 +335,11 @@ static int parse_cmdline_partitions(struct mtd_info *master,
}
offset += part->parts[i].size;
}
*pparts = part->parts;
*pparts = kmemdup(part->parts,
sizeof(*part->parts) * part->num_parts,
GFP_KERNEL);
if (!*pparts)
return -ENOMEM;
return part->num_parts;
}
}
......
......@@ -10,7 +10,6 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/sched.h>
......
......@@ -10,7 +10,6 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
......
......@@ -14,7 +14,6 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
......
......@@ -26,7 +26,6 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
......
......@@ -65,12 +65,6 @@
#define FAST_READ_DUMMY_BYTE 0
#endif
#ifdef CONFIG_MTD_PARTITIONS
#define mtd_has_partitions() (1)
#else
#define mtd_has_partitions() (0)
#endif
/****************************************************************************/
struct m25p {
......@@ -678,6 +672,8 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->mtd.erasesize = info->sector_size;
}
flash->mtd.dev.parent = &spi->dev;
dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
(long long)flash->mtd.size >> 10);
......@@ -708,12 +704,13 @@ static int __devinit m25p_probe(struct spi_device *spi)
struct mtd_partition *parts = NULL;
int nr_parts = 0;
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char *part_probes[] = { "cmdlinepart", NULL, };
if (mtd_has_cmdlinepart()) {
static const char *part_probes[]
= { "cmdlinepart", NULL, };
nr_parts = parse_mtd_partitions(&flash->mtd,
part_probes, &parts, 0);
#endif
nr_parts = parse_mtd_partitions(&flash->mtd,
part_probes, &parts, 0);
}
if (nr_parts <= 0 && data && data->parts) {
parts = data->parts;
......
......@@ -98,12 +98,6 @@ struct dataflash {
struct mtd_info mtd;
};
#ifdef CONFIG_MTD_PARTITIONS
#define mtd_has_partitions() (1)
#else
#define mtd_has_partitions() (0)
#endif
/* ......................................................................... */
/*
......@@ -670,6 +664,8 @@ add_dataflash_otp(struct spi_device *spi, char *name,
device->write = dataflash_write;
device->priv = priv;
device->dev.parent = &spi->dev;
if (revision >= 'c')
otp_tag = otp_setup(device, revision);
......@@ -682,11 +678,13 @@ add_dataflash_otp(struct spi_device *spi, char *name,
struct mtd_partition *parts;
int nr_parts = 0;
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char *part_probes[] = { "cmdlinepart", NULL, };
if (mtd_has_cmdlinepart()) {
static const char *part_probes[]
= { "cmdlinepart", NULL, };
nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0);
#endif
nr_parts = parse_mtd_partitions(device,
part_probes, &parts, 0);
}
if (nr_parts <= 0 && pdata && pdata->parts) {
parts = pdata->parts;
......
......@@ -65,6 +65,19 @@ static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
}
/*
* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
return (unsigned long) mtd->priv + offset;
}
static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
......@@ -116,6 +129,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
mtd->erase = ram_erase;
mtd->point = ram_point;
mtd->unpoint = ram_unpoint;
mtd->get_unmapped_area = ram_get_unmapped_area;
mtd->read = ram_read;
mtd->write = ram_write;
......
......@@ -28,7 +28,6 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
......
/* Internal MTD definitions
*
* Copyright © 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/*
* mtdbdi.c
*/
extern struct backing_dev_info mtd_bdi_unmappable;
extern struct backing_dev_info mtd_bdi_ro_mappable;
extern struct backing_dev_info mtd_bdi_rw_mappable;
......@@ -529,12 +529,6 @@ config MTD_DMV182
help
Map driver for Dy-4 SVME/DMV-182 board.
config MTD_SHARP_SL
tristate "ROM mapped on Sharp SL Series"
depends on ARCH_PXA
help
This enables access to the flash chip on the Sharp SL Series of PDAs.
config MTD_INTEL_VR_NOR
tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
depends on PCI
......@@ -542,6 +536,12 @@ config MTD_INTEL_VR_NOR
Map driver for a NOR flash bank located on the Expansion Bus of the
Intel Vermilion Range chipset.
config MTD_RBTX4939
tristate "Map driver for RBTX4939 board"
depends on TOSHIBA_RBTX4939 && MTD_CFI && MTD_COMPLEX_MAPPINGS
help
Map driver for NOR flash chips on RBTX4939 board.
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
select MTD_RAM
......
......@@ -56,9 +56,9 @@ obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
obj-$(CONFIG_MTD_DMV182) += dmv182.o
obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
......@@ -115,6 +115,8 @@ static int __init omapflash_probe(struct platform_device *pdev)
}
info->mtd->owner = THIS_MODULE;
info->mtd->dev.parent = &pdev->dev;
#ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0);
if (err > 0)
......
......@@ -147,6 +147,7 @@ static int physmap_flash_probe(struct platform_device *dev)
devices_found++;
}
info->mtd[i]->owner = THIS_MODULE;
info->mtd[i]->dev.parent = &dev->dev;
}
if (devices_found == 1) {
......
......@@ -219,6 +219,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
goto err_out;
}
info->mtd->owner = THIS_MODULE;
info->mtd->dev.parent = &dev->dev;
#ifdef CONFIG_MTD_PARTITIONS
/* First look for RedBoot table or partitions on the command
......
......@@ -224,6 +224,7 @@ static int platram_probe(struct platform_device *pdev)
}
info->mtd->owner = THIS_MODULE;
info->mtd->dev.parent = &pdev->dev;
platram_setrw(info, PLATRAM_RW);
......
/*
* rbtx4939-flash (based on physmap.c)
*
* This is a simplified physmap driver with map_init callback function.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Copyright (C) 2009 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/txx9/rbtx4939.h>
struct rbtx4939_flash_info {
struct mtd_info *mtd;
struct map_info map;
#ifdef CONFIG_MTD_PARTITIONS
int nr_parts;
struct mtd_partition *parts;
#endif
};
static int rbtx4939_flash_remove(struct platform_device *dev)
{
struct rbtx4939_flash_info *info;
info = platform_get_drvdata(dev);
if (!info)
return 0;
platform_set_drvdata(dev, NULL);
if (info->mtd) {
#ifdef CONFIG_MTD_PARTITIONS
struct rbtx4939_flash_data *pdata = dev->dev.platform_data;
if (info->nr_parts) {
del_mtd_partitions(info->mtd);
kfree(info->parts);
} else if (pdata->nr_parts)
del_mtd_partitions(info->mtd);
else
del_mtd_device(info->mtd);
#else
del_mtd_device(info->mtd);
#endif
map_destroy(info->mtd);
}
return 0;
}
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probe_types[] = { "cmdlinepart", NULL };
#endif
static int rbtx4939_flash_probe(struct platform_device *dev)
{
struct rbtx4939_flash_data *pdata;
struct rbtx4939_flash_info *info;
struct resource *res;
const char **probe_type;
int err = 0;
unsigned long size;
pdata = dev->dev.platform_data;
if (!pdata)
return -ENODEV;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
info = devm_kzalloc(&dev->dev, sizeof(struct rbtx4939_flash_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(dev, info);
size = resource_size(res);
pr_notice("rbtx4939 platform flash device: %pR\n", res);
if (!devm_request_mem_region(&dev->dev, res->start, size,
dev_name(&dev->dev)))
return -EBUSY;
info->map.name = dev_name(&dev->dev);
info->map.phys = res->start;
info->map.size = size;
info->map.bankwidth = pdata->width;
info->map.virt = devm_ioremap(&dev->dev, info->map.phys, size);
if (!info->map.virt)
return -EBUSY;
if (pdata->map_init)
(*pdata->map_init)(&info->map);
else
simple_map_init(&info->map);
probe_type = rom_probe_types;
for (; !info->mtd && *probe_type; probe_type++)
info->mtd = do_map_probe(*probe_type, &info->map);
if (!info->mtd) {
dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO;
goto err_out;
}
info->mtd->owner = THIS_MODULE;
if (err)
goto err_out;
#ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions(info->mtd, part_probe_types,
&info->parts, 0);
if (err > 0) {
add_mtd_partitions(info->mtd, info->parts, err);
info->nr_parts = err;
return 0;
}
if (pdata->nr_parts) {
pr_notice("Using rbtx4939 partition information\n");
add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts);
return 0;
}
#endif
add_mtd_device(info->mtd);
return 0;
err_out:
rbtx4939_flash_remove(dev);
return err;
}
#ifdef CONFIG_PM
static int rbtx4939_flash_suspend(struct platform_device *dev,
pm_message_t state)
{
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
if (info->mtd->suspend)
return info->mtd->suspend(info->mtd);
return 0;
}
static int rbtx4939_flash_resume(struct platform_device *dev)
{
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
if (info->mtd->resume)
info->mtd->resume(info->mtd);
return 0;
}
static void rbtx4939_flash_shutdown(struct platform_device *dev)
{
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
if (info->mtd->suspend && info->mtd->resume)
if (info->mtd->suspend(info->mtd) == 0)
info->mtd->resume(info->mtd);
}
#else
#define rbtx4939_flash_suspend NULL
#define rbtx4939_flash_resume NULL
#define rbtx4939_flash_shutdown NULL
#endif
static struct platform_driver rbtx4939_flash_driver = {
.probe = rbtx4939_flash_probe,
.remove = rbtx4939_flash_remove,
.suspend = rbtx4939_flash_suspend,
.resume = rbtx4939_flash_resume,
.shutdown = rbtx4939_flash_shutdown,
.driver = {
.name = "rbtx4939-flash",
.owner = THIS_MODULE,
},
};
static int __init rbtx4939_flash_init(void)
{
return platform_driver_register(&rbtx4939_flash_driver);
}
static void __exit rbtx4939_flash_exit(void)
{
platform_driver_unregister(&rbtx4939_flash_driver);
}
module_init(rbtx4939_flash_init);
module_exit(rbtx4939_flash_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RBTX4939 MTD map driver");
MODULE_ALIAS("platform:rbtx4939-flash");
......@@ -351,7 +351,7 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
static int __init sa1100_mtd_probe(struct platform_device *pdev)
static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
{
struct flash_platform_data *plat = pdev->dev.platform_data;
struct mtd_partition *parts;
......
/*
* sharpsl-flash.c
*
* Copyright (C) 2001 Lineo Japan, Inc.
* Copyright (C) 2002 SHARP
*
* based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp
* Handle mapping of the flash on the RPX Lite and CLLF boards
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/mach-types.h>
#define WINDOW_ADDR 0x00000000
#define WINDOW_SIZE 0x00800000
#define BANK_WIDTH 2
static struct mtd_info *mymtd;
struct map_info sharpsl_map = {
.name = "sharpsl-flash",
.size = WINDOW_SIZE,
.bankwidth = BANK_WIDTH,
.phys = WINDOW_ADDR
};
static struct mtd_partition sharpsl_partitions[1] = {
{
name: "Boot PROM Filesystem",
}
};
static int __init init_sharpsl(void)
{
struct mtd_partition *parts;
int nb_parts = 0;
char *part_type = "static";
printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n",
WINDOW_SIZE, WINDOW_ADDR);
sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!sharpsl_map.virt) {
printk("Failed to ioremap\n");
return -EIO;
}
simple_map_init(&sharpsl_map);
mymtd = do_map_probe("map_rom", &sharpsl_map);
if (!mymtd) {
iounmap(sharpsl_map.virt);
return -ENXIO;
}
mymtd->owner = THIS_MODULE;
if (machine_is_corgi() || machine_is_shepherd() || machine_is_husky()
|| machine_is_poodle()) {
sharpsl_partitions[0].size=0x006d0000;
sharpsl_partitions[0].offset=0x00120000;
} else if (machine_is_tosa()) {
sharpsl_partitions[0].size=0x006a0000;
sharpsl_partitions[0].offset=0x00160000;
} else if (machine_is_spitz() || machine_is_akita() || machine_is_borzoi()) {
sharpsl_partitions[0].size=0x006b0000;
sharpsl_partitions[0].offset=0x00140000;
} else {
map_destroy(mymtd);
iounmap(sharpsl_map.virt);
return -ENODEV;
}
parts = sharpsl_partitions;
nb_parts = ARRAY_SIZE(sharpsl_partitions);
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(mymtd, parts, nb_parts);
return 0;
}
static void __exit cleanup_sharpsl(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
if (sharpsl_map.virt) {
iounmap(sharpsl_map.virt);
sharpsl_map.virt = 0;
}
}
module_init(init_sharpsl);
module_exit(cleanup_sharpsl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SHARP (Original: Arnold Christensen <AKC@pel.dk>)");
MODULE_DESCRIPTION("MTD map driver for SHARP SL series");
......@@ -286,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
gd->private_data = new;
new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq;
gd->driverfs_dev = new->mtd->dev.parent;
if (new->readonly)
set_disk_ro(gd, 1);
......@@ -382,11 +383,12 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
"%sd", tr->name);
if (IS_ERR(tr->blkcore_priv->thread)) {
int ret = PTR_ERR(tr->blkcore_priv->thread);
blk_cleanup_queue(tr->blkcore_priv->rq);
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
mutex_unlock(&mtd_table_mutex);
return PTR_ERR(tr->blkcore_priv->thread);
return ret;
}
INIT_LIST_HEAD(&tr->devs);
......
/* MTD backing device capabilities
*
* Copyright © 2006 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/backing-dev.h>
#include <linux/mtd/mtd.h>
#include "internal.h"
/*
* backing device capabilities for non-mappable devices (such as NAND flash)
* - permits private mappings, copies are taken of the data
*/
struct backing_dev_info mtd_bdi_unmappable = {
.capabilities = BDI_CAP_MAP_COPY,
};
/*
* backing device capabilities for R/O mappable devices (such as ROM)
* - permits private mappings, copies are taken of the data
* - permits non-writable shared mappings
*/
struct backing_dev_info mtd_bdi_ro_mappable = {
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP),
};
/*
* backing device capabilities for writable mappable devices (such as RAM)
* - permits private mappings, copies are taken of the data
* - permits non-writable shared mappings
*/
struct backing_dev_info mtd_bdi_rw_mappable = {
.capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP |
BDI_CAP_WRITE_MAP),
};
......@@ -13,39 +13,13 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/backing-dev.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
#include <asm/uaccess.h>
static struct class *mtd_class;
static void mtd_notify_add(struct mtd_info* mtd)
{
if (!mtd)
return;
device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
NULL, "mtd%d", mtd->index);
device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
NULL, "mtd%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
{
if (!mtd)
return;
device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
}
static struct mtd_notifier notifier = {
.add = mtd_notify_add,
.remove = mtd_notify_remove,
};
/*
* Data structure to hold the pointer to the mtd device as well
......@@ -107,12 +81,15 @@ static int mtd_open(struct inode *inode, struct file *file)
goto out;
}
if (MTD_ABSENT == mtd->type) {
if (mtd->type == MTD_ABSENT) {
put_mtd_device(mtd);
ret = -ENODEV;
goto out;
}
if (mtd->backing_dev_info)
file->f_mapping->backing_dev_info = mtd->backing_dev_info;
/* You can't open it RW if it's not a writeable device */
if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
put_mtd_device(mtd);
......@@ -781,6 +758,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
return ret;
} /* memory_ioctl */
/*
* try to determine where a shared mapping can be made
* - only supported for NOMMU at the moment (MMU can't doesn't copy private
* mappings)
*/
#ifndef CONFIG_MMU
static unsigned long mtd_get_unmapped_area(struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
unsigned long flags)
{
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
if (mtd->get_unmapped_area) {
unsigned long offset;
if (addr != 0)
return (unsigned long) -EINVAL;
if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
return (unsigned long) -EINVAL;
offset = pgoff << PAGE_SHIFT;
if (offset > mtd->size - len)
return (unsigned long) -EINVAL;
return mtd->get_unmapped_area(mtd, len, offset, flags);
}
/* can't map directly */
return (unsigned long) -ENOSYS;
}
#endif
/*
* set up a mapping for shared memory segments
*/
static int mtd_mmap(struct file *file, struct vm_area_struct *vma)
{
#ifdef CONFIG_MMU
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM)
return 0;
return -ENOSYS;
#else
return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
#endif
}
static const struct file_operations mtd_fops = {
.owner = THIS_MODULE,
.llseek = mtd_lseek,
......@@ -789,39 +819,36 @@ static const struct file_operations mtd_fops = {
.ioctl = mtd_ioctl,
.open = mtd_open,
.release = mtd_close,
.mmap = mtd_mmap,
#ifndef CONFIG_MMU
.get_unmapped_area = mtd_get_unmapped_area,
#endif
};
static int __init init_mtdchar(void)
{
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
int status;
status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);
if (status < 0) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
mtd_class = class_create(THIS_MODULE, "mtd");
if (IS_ERR(mtd_class)) {
printk(KERN_ERR "Error creating mtd class.\n");
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
return PTR_ERR(mtd_class);
}
register_mtd_user(&notifier);
return 0;
return status;
}
static void __exit cleanup_mtdchar(void)
{
unregister_mtd_user(&notifier);
class_destroy(mtd_class);
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
}
module_init(init_mtdchar);
module_exit(cleanup_mtdchar);
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Direct character-device access to MTD devices");
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
......@@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/backing-dev.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/concat.h>
......@@ -683,6 +684,40 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
return err;
}
/*
* try to support NOMMU mmaps on concatenated devices
* - we don't support subdev spanning as we can't guarantee it'll work
*/
static unsigned long concat_get_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct mtd_concat *concat = CONCAT(mtd);
int i;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
if (offset >= subdev->size) {
offset -= subdev->size;
continue;
}
/* we've found the subdev over which the mapping will reside */
if (offset + len > subdev->size)
return (unsigned long) -EINVAL;
if (subdev->get_unmapped_area)
return subdev->get_unmapped_area(subdev, len, offset,
flags);
break;
}
return (unsigned long) -ENOSYS;
}
/*
* This function constructs a virtual MTD device by concatenating
* num_devs MTD devices. A pointer to the new device object is
......@@ -740,6 +775,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
concat->mtd.backing_dev_info = subdev[0]->backing_dev_info;
concat->subdev[0] = subdev[0];
for (i = 1; i < num_devs; i++) {
......@@ -766,6 +803,15 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.flags |=
subdev[i]->flags & MTD_WRITEABLE;
}
/* only permit direct mapping if the BDIs are all the same
* - copy-mapping is still permitted
*/
if (concat->mtd.backing_dev_info !=
subdev[i]->backing_dev_info)
concat->mtd.backing_dev_info =
&default_backing_dev_info;
concat->mtd.size += subdev[i]->size;
concat->mtd.ecc_stats.badblocks +=
subdev[i]->ecc_stats.badblocks;
......@@ -796,6 +842,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.unlock = concat_unlock;
concat->mtd.suspend = concat_suspend;
concat->mtd.resume = concat_resume;
concat->mtd.get_unmapped_area = concat_get_unmapped_area;
/*
* Combine the erase block size info of the subdevices:
......
......@@ -19,9 +19,13 @@
#include <linux/proc_fs.h>
#include <linux/mtd/mtd.h>
#include "internal.h"
#include "mtdcore.h"
static struct class *mtd_class;
/* These are exported solely for the purpose of mtd_blkdevs.c. You
should not use them for _anything_ else */
DEFINE_MUTEX(mtd_table_mutex);
......@@ -32,6 +36,160 @@ EXPORT_SYMBOL_GPL(mtd_table);
static LIST_HEAD(mtd_notifiers);
#if defined(CONFIG_MTD_CHAR) || defined(CONFIG_MTD_CHAR_MODULE)
#define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
#else
#define MTD_DEVT(index) 0
#endif
/* REVISIT once MTD uses the driver model better, whoever allocates
* the mtd_info will probably want to use the release() hook...
*/
static void mtd_release(struct device *dev)
{
struct mtd_info *mtd = dev_to_mtd(dev);
/* remove /dev/mtdXro node if needed */
if (MTD_DEVT(mtd->index))
device_destroy(mtd_class, MTD_DEVT(mtd->index) + 1);
}
static ssize_t mtd_type_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
char *type;
switch (mtd->type) {
case MTD_ABSENT:
type = "absent";
break;
case MTD_RAM:
type = "ram";
break;
case MTD_ROM:
type = "rom";
break;
case MTD_NORFLASH:
type = "nor";
break;
case MTD_NANDFLASH:
type = "nand";
break;
case MTD_DATAFLASH:
type = "dataflash";
break;
case MTD_UBIVOLUME:
type = "ubi";
break;
default:
type = "unknown";
}
return snprintf(buf, PAGE_SIZE, "%s\n", type);
}
static DEVICE_ATTR(type, S_IRUGO, mtd_type_show, NULL);
static ssize_t mtd_flags_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)mtd->flags);
}
static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL);
static ssize_t mtd_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "%llu\n",
(unsigned long long)mtd->size);
}
static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL);
static ssize_t mtd_erasesize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->erasesize);
}
static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL);
static ssize_t mtd_writesize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->writesize);
}
static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL);
static ssize_t mtd_oobsize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->oobsize);
}
static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL);
static ssize_t mtd_numeraseregions_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "%u\n", mtd->numeraseregions);
}
static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show,
NULL);
static ssize_t mtd_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_to_mtd(dev);
return snprintf(buf, PAGE_SIZE, "%s\n", mtd->name);
}
static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL);
static struct attribute *mtd_attrs[] = {
&dev_attr_type.attr,
&dev_attr_flags.attr,
&dev_attr_size.attr,
&dev_attr_erasesize.attr,
&dev_attr_writesize.attr,
&dev_attr_oobsize.attr,
&dev_attr_numeraseregions.attr,
&dev_attr_name.attr,
NULL,
};
struct attribute_group mtd_group = {
.attrs = mtd_attrs,
};
struct attribute_group *mtd_groups[] = {
&mtd_group,
NULL,
};
static struct device_type mtd_devtype = {
.name = "mtd",
.groups = mtd_groups,
.release = mtd_release,
};
/**
* add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure
......@@ -40,12 +198,27 @@ static LIST_HEAD(mtd_notifiers);
* notify each currently active MTD 'user' of its arrival. Returns
* zero on success or 1 on failure, which currently will only happen
* if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
* or there's a sysfs error.
*/
int add_mtd_device(struct mtd_info *mtd)
{
int i;
if (!mtd->backing_dev_info) {
switch (mtd->type) {
case MTD_RAM:
mtd->backing_dev_info = &mtd_bdi_rw_mappable;
break;
case MTD_ROM:
mtd->backing_dev_info = &mtd_bdi_ro_mappable;
break;
default:
mtd->backing_dev_info = &mtd_bdi_unmappable;
break;
}
}
BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex);
......@@ -80,6 +253,23 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->name);
}
/* Caller should have set dev.parent to match the
* physical device.
*/
mtd->dev.type = &mtd_devtype;
mtd->dev.class = mtd_class;
mtd->dev.devt = MTD_DEVT(i);
dev_set_name(&mtd->dev, "mtd%d", i);
if (device_register(&mtd->dev) != 0) {
mtd_table[i] = NULL;
break;
}
if (MTD_DEVT(i))
device_create(mtd_class, mtd->dev.parent,
MTD_DEVT(i) + 1,
NULL, "mtd%dro", i);
DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
......@@ -124,6 +314,8 @@ int del_mtd_device (struct mtd_info *mtd)
} else {
struct mtd_notifier *not;
device_unregister(&mtd->dev);
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
list_for_each_entry(not, &mtd_notifiers, list)
......@@ -393,28 +585,38 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count,
return ((count < begin+len-off) ? count : begin+len-off);
}
#endif /* CONFIG_PROC_FS */
/*====================================================================*/
/* Init code */
static int __init init_mtd(void)
{
mtd_class = class_create(THIS_MODULE, "mtd");
if (IS_ERR(mtd_class)) {
pr_err("Error creating mtd class.\n");
return PTR_ERR(mtd_class);
}
#ifdef CONFIG_PROC_FS
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
proc_mtd->read_proc = mtd_read_proc;
#endif /* CONFIG_PROC_FS */
return 0;
}
static void __exit cleanup_mtd(void)
{
#ifdef CONFIG_PROC_FS
if (proc_mtd)
remove_proc_entry( "mtd", NULL);
#endif /* CONFIG_PROC_FS */
class_destroy(mtd_class);
}
module_init(init_mtd);
module_exit(cleanup_mtd);
#endif /* CONFIG_PROC_FS */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Core MTD registration and access routines");
......@@ -44,6 +44,7 @@ static struct mtdoops_context {
int oops_pages;
int nextpage;
int nextcount;
char *name;
void *oops_buf;
......@@ -273,6 +274,9 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
{
struct mtdoops_context *cxt = &oops_cxt;
if (cxt->name && !strcmp(mtd->name, cxt->name))
cxt->mtd_index = mtd->index;
if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
return;
......@@ -357,8 +361,10 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
spin_lock_irqsave(&cxt->writecount_lock, flags);
/* Check ready status didn't change whilst waiting for the lock */
if (!cxt->ready)
if (!cxt->ready) {
spin_unlock_irqrestore(&cxt->writecount_lock, flags);
return;
}
if (cxt->writecount == 0) {
u32 *stamp = cxt->oops_buf;
......@@ -383,8 +389,12 @@ static int __init mtdoops_console_setup(struct console *co, char *options)
{
struct mtdoops_context *cxt = co->data;
if (cxt->mtd_index != -1)
if (cxt->mtd_index != -1 || cxt->name)
return -EBUSY;
if (options) {
cxt->name = kstrdup(options, GFP_KERNEL);
return 0;
}
if (co->index == -1)
return -EINVAL;
......@@ -412,6 +422,7 @@ static int __init mtdoops_console_init(void)
cxt->mtd_index = -1;
cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
spin_lock_init(&cxt->writecount_lock);
if (!cxt->oops_buf) {
printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n");
......@@ -432,6 +443,7 @@ static void __exit mtdoops_console_exit(void)
unregister_mtd_user(&mtdoops_notifier);
unregister_console(&mtdoops_console);
kfree(cxt->name);
vfree(cxt->oops_buf);
}
......
......@@ -48,8 +48,11 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct mtd_part *part = PART(mtd);
struct mtd_ecc_stats stats;
int res;
stats = part->master->ecc_stats;
if (from >= mtd->size)
len = 0;
else if (from + len > mtd->size)
......@@ -58,9 +61,9 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
len, retlen, buf);
if (unlikely(res)) {
if (res == -EUCLEAN)
mtd->ecc_stats.corrected++;
mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
if (res == -EBADMSG)
mtd->ecc_stats.failed++;
mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
}
return res;
}
......@@ -84,6 +87,18 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
part->master->unpoint(part->master, from + part->offset, len);
}
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct mtd_part *part = PART(mtd);
offset += part->offset;
return part->master->get_unmapped_area(part->master, len, offset,
flags);
}
static int part_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
......@@ -342,6 +357,12 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.name = part->name;
slave->mtd.owner = master->owner;
slave->mtd.backing_dev_info = master->backing_dev_info;
/* NOTE: we don't arrange MTDs as a tree; it'd be error-prone
* to have the same data be in two different partitions.
*/
slave->mtd.dev.parent = master->dev.parent;
slave->mtd.read = part_read;
slave->mtd.write = part_write;
......@@ -354,6 +375,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
slave->mtd.unpoint = part_unpoint;
}
if (master->get_unmapped_area)
slave->mtd.get_unmapped_area = part_get_unmapped_area;
if (master->read_oob)
slave->mtd.read_oob = part_read_oob;
if (master->write_oob)
......@@ -493,7 +516,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
* 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
* the partition definitions.
* (Q: should we register the master MTD object as well?)
*
* We don't register the master, or expect the caller to have done so,
* for reasons of data integrity.
*/
int add_mtd_partitions(struct mtd_info *master,
......
......@@ -334,7 +334,7 @@ config MTD_NAND_ATMEL_ECC_NONE
endchoice
config MTD_NAND_PXA3xx
bool "Support for NAND flash devices on PXA3xx"
tristate "Support for NAND flash devices on PXA3xx"
depends on MTD_NAND && PXA3xx
help
This enables the driver for the NAND flash device found on
......@@ -427,4 +427,23 @@ config MTD_NAND_SH_FLCTL
Several Renesas SuperH CPU has FLCTL. This option enables support
for NAND Flash using FLCTL. This driver support SH7723.
config MTD_NAND_DAVINCI
tristate "Support NAND on DaVinci SoC"
depends on ARCH_DAVINCI
help
Enable the driver for NAND flash chips on Texas Instruments
DaVinci processors.
config MTD_NAND_TXX9NDFMC
tristate "NAND Flash support for TXx9 SoC"
depends on SOC_TX4938 || SOC_TX4939
help
This enables the NAND flash controller on the TXx9 SoCs.
config MTD_NAND_SOCRATES
tristate "Support for NAND on Socrates board"
depends on MTD_NAND && SOCRATES
help
Enables support for NAND Flash chips wired onto Socrates board.
endif # MTD_NAND
......@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
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_H1900) += h1910.o
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
......@@ -36,5 +37,7 @@ obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o
obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
nand-objs := nand_base.o nand_bbt.o
......@@ -552,7 +552,6 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
{
int ret;
unsigned short val;
/* Do not use dma */
if (!hardware_ecc)
......@@ -560,13 +559,6 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
init_completion(&info->dma_completion);
#ifdef CONFIG_BF54x
/* Setup DMAC1 channel mux for NFC which shared with SDH */
val = bfin_read_DMAC1_PERIMUX();
val &= 0xFFFE;
bfin_write_DMAC1_PERIMUX(val);
SSYNC();
#endif
/* Request NFC DMA channel */
ret = request_dma(CH_NFC, "BF5XX NFC driver");
if (ret < 0) {
......@@ -574,7 +566,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
return ret;
}
set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info);
#ifdef CONFIG_BF54x
/* Setup DMAC1 channel mux for NFC which shared with SDH */
bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1);
SSYNC();
#endif
set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info);
/* Turn off the DMA channel first */
disable_dma(CH_NFC);
......@@ -632,7 +630,7 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
/*
* Device management interface
*/
static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
{
struct mtd_info *mtd = &info->mtd;
......
......@@ -654,6 +654,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
}
cafe = (void *)(&mtd[1]);
mtd->dev.parent = &pdev->dev;
mtd->priv = cafe;
mtd->owner = THIS_MODULE;
......
This diff is collapsed.
......@@ -23,6 +23,10 @@
#include <linux/io.h>
#include <asm/fsl_lbc.h>
#define FSL_UPM_WAIT_RUN_PATTERN 0x1
#define FSL_UPM_WAIT_WRITE_BYTE 0x2
#define FSL_UPM_WAIT_WRITE_BUFFER 0x4
struct fsl_upm_nand {
struct device *dev;
struct mtd_info mtd;
......@@ -36,8 +40,12 @@ struct fsl_upm_nand {
uint8_t upm_addr_offset;
uint8_t upm_cmd_offset;
void __iomem *io_base;
int rnb_gpio;
int rnb_gpio[NAND_MAX_CHIPS];
uint32_t mchip_offsets[NAND_MAX_CHIPS];
uint32_t mchip_count;
uint32_t mchip_number;
int chip_delay;
uint32_t wait_flags;
};
#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
......@@ -46,7 +54,7 @@ static int fun_chip_ready(struct mtd_info *mtd)
{
struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
if (gpio_get_value(fun->rnb_gpio))
if (gpio_get_value(fun->rnb_gpio[fun->mchip_number]))
return 1;
dev_vdbg(fun->dev, "busy\n");
......@@ -55,9 +63,9 @@ static int fun_chip_ready(struct mtd_info *mtd)
static void fun_wait_rnb(struct fsl_upm_nand *fun)
{
int cnt = 1000000;
if (fun->rnb_gpio[fun->mchip_number] >= 0) {
int cnt = 1000000;
if (fun->rnb_gpio >= 0) {
while (--cnt && !fun_chip_ready(&fun->mtd))
cpu_relax();
if (!cnt)
......@@ -69,7 +77,9 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun)
static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
u32 mar;
if (!(ctrl & fun->last_ctrl)) {
fsl_upm_end_pattern(&fun->upm);
......@@ -87,9 +97,28 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
}
fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd);
mar = (cmd << (32 - fun->upm.width)) |
fun->mchip_offsets[fun->mchip_number];
fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar);
if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
fun_wait_rnb(fun);
}
static void fun_select_chip(struct mtd_info *mtd, int mchip_nr)
{
struct nand_chip *chip = mtd->priv;
struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
fun_wait_rnb(fun);
if (mchip_nr == -1) {
chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
} else if (mchip_nr >= 0) {
fun->mchip_number = mchip_nr;
chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
chip->IO_ADDR_W = chip->IO_ADDR_R;
} else {
BUG();
}
}
static uint8_t fun_read_byte(struct mtd_info *mtd)
......@@ -115,8 +144,11 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
for (i = 0; i < len; i++) {
out_8(fun->chip.IO_ADDR_W, buf[i]);
fun_wait_rnb(fun);
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
fun_wait_rnb(fun);
}
if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
fun_wait_rnb(fun);
}
static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
......@@ -137,8 +169,10 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
fun->chip.read_buf = fun_read_buf;
fun->chip.write_buf = fun_write_buf;
fun->chip.ecc.mode = NAND_ECC_SOFT;
if (fun->mchip_count > 1)
fun->chip.select_chip = fun_select_chip;
if (fun->rnb_gpio >= 0)
if (fun->rnb_gpio[0] >= 0)
fun->chip.dev_ready = fun_chip_ready;
fun->mtd.priv = &fun->chip;
......@@ -155,7 +189,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
goto err;
}
ret = nand_scan(&fun->mtd, 1);
ret = nand_scan(&fun->mtd, fun->mchip_count);
if (ret)
goto err;
......@@ -185,8 +219,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
struct fsl_upm_nand *fun;
struct resource io_res;
const uint32_t *prop;
int rnb_gpio;
int ret;
int size;
int i;
fun = kzalloc(sizeof(*fun), GFP_KERNEL);
if (!fun)
......@@ -208,7 +244,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
if (!prop || size != sizeof(uint32_t)) {
dev_err(&ofdev->dev, "can't get UPM address offset\n");
ret = -EINVAL;
goto err2;
goto err1;
}
fun->upm_addr_offset = *prop;
......@@ -216,21 +252,40 @@ static int __devinit fun_probe(struct of_device *ofdev,
if (!prop || size != sizeof(uint32_t)) {
dev_err(&ofdev->dev, "can't get UPM command offset\n");
ret = -EINVAL;
goto err2;
goto err1;
}
fun->upm_cmd_offset = *prop;
fun->rnb_gpio = of_get_gpio(ofdev->node, 0);
if (fun->rnb_gpio >= 0) {
ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev));
if (ret) {
dev_err(&ofdev->dev, "can't request RNB gpio\n");
prop = of_get_property(ofdev->node,
"fsl,upm-addr-line-cs-offsets", &size);
if (prop && (size / sizeof(uint32_t)) > 0) {
fun->mchip_count = size / sizeof(uint32_t);
if (fun->mchip_count >= NAND_MAX_CHIPS) {
dev_err(&ofdev->dev, "too much multiple chips\n");
goto err1;
}
for (i = 0; i < fun->mchip_count; i++)
fun->mchip_offsets[i] = prop[i];
} else {
fun->mchip_count = 1;
}
for (i = 0; i < fun->mchip_count; i++) {
fun->rnb_gpio[i] = -1;
rnb_gpio = of_get_gpio(ofdev->node, i);
if (rnb_gpio >= 0) {
ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev));
if (ret) {
dev_err(&ofdev->dev,
"can't request RNB gpio #%d\n", i);
goto err2;
}
gpio_direction_input(rnb_gpio);
fun->rnb_gpio[i] = rnb_gpio;
} else if (rnb_gpio == -EINVAL) {
dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
goto err2;
}
gpio_direction_input(fun->rnb_gpio);
} else if (fun->rnb_gpio == -EINVAL) {
dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
goto err2;
}
prop = of_get_property(ofdev->node, "chip-delay", NULL);
......@@ -239,8 +294,15 @@ static int __devinit fun_probe(struct of_device *ofdev,
else
fun->chip_delay = 50;
prop = of_get_property(ofdev->node, "fsl,upm-wait-flags", &size);
if (prop && size == sizeof(uint32_t))
fun->wait_flags = *prop;
else
fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
FSL_UPM_WAIT_WRITE_BYTE;
fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
io_res.end - io_res.start + 1);
io_res.end - io_res.start + 1);
if (!fun->io_base) {
ret = -ENOMEM;
goto err2;
......@@ -257,8 +319,11 @@ static int __devinit fun_probe(struct of_device *ofdev,
return 0;
err2:
if (fun->rnb_gpio >= 0)
gpio_free(fun->rnb_gpio);
for (i = 0; i < fun->mchip_count; i++) {
if (fun->rnb_gpio[i] < 0)
break;
gpio_free(fun->rnb_gpio[i]);
}
err1:
kfree(fun);
......@@ -268,12 +333,16 @@ static int __devinit fun_probe(struct of_device *ofdev,
static int __devexit fun_remove(struct of_device *ofdev)
{
struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
int i;
nand_release(&fun->mtd);
kfree(fun->mtd.name);
if (fun->rnb_gpio >= 0)
gpio_free(fun->rnb_gpio);
for (i = 0; i < fun->mchip_count; i++) {
if (fun->rnb_gpio[i] < 0)
break;
gpio_free(fun->rnb_gpio[i]);
}
kfree(fun);
......
......@@ -866,6 +866,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
mtd = &host->mtd;
mtd->priv = this;
mtd->owner = THIS_MODULE;
mtd->dev.parent = &pdev->dev;
/* 50 us command delay time */
this->chip_delay = 5;
......
......@@ -82,6 +82,20 @@ static struct nand_ecclayout nand_oob_64 = {
.length = 38}}
};
static struct nand_ecclayout nand_oob_128 = {
.eccbytes = 48,
.eccpos = {
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 101, 102, 103,
104, 105, 106, 107, 108, 109, 110, 111,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127},
.oobfree = {
{.offset = 2,
.length = 78}}
};
static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
int new_state);
......@@ -748,6 +762,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
*
* Not for syndrome calculating ecc controllers, which use a special oob layout
*/
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf)
......@@ -757,6 +773,47 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
/**
* nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
*
* We need a special oob layout and handling even when OOB isn't used.
*/
static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf)
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
uint8_t *oob = chip->oob_poi;
int steps, size;
for (steps = chip->ecc.steps; steps > 0; steps--) {
chip->read_buf(mtd, buf, eccsize);
buf += eccsize;
if (chip->ecc.prepad) {
chip->read_buf(mtd, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
chip->read_buf(mtd, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
chip->read_buf(mtd, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
size = mtd->oobsize - (oob - chip->oob_poi);
if (size)
chip->read_buf(mtd, oob, size);
return 0;
}
/**
* nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: mtd info structure
......@@ -1482,6 +1539,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
*
* Not for syndrome calculating ecc controllers, which use a special oob layout
*/
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
......@@ -1490,6 +1549,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
}
/**
* nand_write_page_raw_syndrome - [Intern] raw page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
*
* We need a special oob layout and handling even when ECC isn't checked.
*/
static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
int eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
uint8_t *oob = chip->oob_poi;
int steps, size;
for (steps = chip->ecc.steps; steps > 0; steps--) {
chip->write_buf(mtd, buf, eccsize);
buf += eccsize;
if (chip->ecc.prepad) {
chip->write_buf(mtd, oob, chip->ecc.prepad);
oob += chip->ecc.prepad;
}
chip->read_buf(mtd, oob, eccbytes);
oob += eccbytes;
if (chip->ecc.postpad) {
chip->write_buf(mtd, oob, chip->ecc.postpad);
oob += chip->ecc.postpad;
}
}
size = mtd->oobsize - (oob - chip->oob_poi);
if (size)
chip->write_buf(mtd, oob, size);
}
/**
* nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: mtd info structure
......@@ -1863,7 +1960,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
}
if (unlikely(ops->ooboffs >= len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: "
"Attempt to start write outside oob\n");
return -EINVAL;
}
......@@ -1873,7 +1970,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
ops->ooboffs + ops->ooblen >
((mtd->size >> chip->page_shift) -
(to >> chip->page_shift)) * len)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: "
"Attempt write beyond end of device\n");
return -EINVAL;
}
......@@ -1929,8 +2026,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
/* Do not allow writes past end of device */
if (ops->datbuf && (to + ops->len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt read beyond end of device\n");
DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
"Attempt write beyond end of device\n");
return -EINVAL;
}
......@@ -2555,6 +2652,9 @@ int nand_scan_tail(struct mtd_info *mtd)
case 64:
chip->ecc.layout = &nand_oob_64;
break;
case 128:
chip->ecc.layout = &nand_oob_128;
break;
default:
printk(KERN_WARNING "No oob scheme defined for "
"oobsize %d\n", mtd->oobsize);
......@@ -2569,10 +2669,6 @@ int nand_scan_tail(struct mtd_info *mtd)
* check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw;
switch (chip->ecc.mode) {
case NAND_ECC_HW:
......@@ -2581,6 +2677,10 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_page = nand_read_page_hwecc;
if (!chip->ecc.write_page)
chip->ecc.write_page = nand_write_page_hwecc;
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw;
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_std;
if (!chip->ecc.write_oob)
......@@ -2602,6 +2702,10 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_page = nand_read_page_syndrome;
if (!chip->ecc.write_page)
chip->ecc.write_page = nand_write_page_syndrome;
if (!chip->ecc.read_page_raw)
chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
if (!chip->ecc.write_page_raw)
chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
if (!chip->ecc.read_oob)
chip->ecc.read_oob = nand_read_oob_syndrome;
if (!chip->ecc.write_oob)
......@@ -2620,6 +2724,8 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_page = nand_read_page_swecc;
chip->ecc.read_subpage = nand_read_subpage;
chip->ecc.write_page = nand_write_page_swecc;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.write_oob = nand_write_oob_std;
chip->ecc.size = 256;
......@@ -2632,6 +2738,8 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.read_page = nand_read_page_raw;
chip->ecc.write_page = nand_write_page_raw;
chip->ecc.read_oob = nand_read_oob_std;
chip->ecc.read_page_raw = nand_read_page_raw;
chip->ecc.write_page_raw = nand_write_page_raw;
chip->ecc.write_oob = nand_write_oob_std;
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
......@@ -2676,6 +2784,7 @@ int nand_scan_tail(struct mtd_info *mtd)
break;
case 4:
case 8:
case 16:
mtd->subpage_sft = 2;
break;
}
......
......@@ -30,7 +30,7 @@ struct plat_nand_data {
/*
* Probe for the NAND device.
*/
static int __init plat_nand_probe(struct platform_device *pdev)
static int __devinit plat_nand_probe(struct platform_device *pdev)
{
struct platform_nand_data *pdata = pdev->dev.platform_data;
struct plat_nand_data *data;
......
......@@ -170,7 +170,13 @@ static int use_dma = 1;
module_param(use_dma, bool, 0444);
MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
/*
* Default NAND flash controller configuration setup by the
* bootloader. This configuration is used only when pdata->keep_config is set
*/
static struct pxa3xx_nand_timing default_timing;
static struct pxa3xx_nand_flash default_flash;
static struct pxa3xx_nand_cmdset smallpage_cmdset = {
.read1 = 0x0000,
.read2 = 0x0050,
......@@ -197,6 +203,7 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = {
.lock_status = 0x007A,
};
#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
static struct pxa3xx_nand_timing samsung512MbX16_timing = {
.tCH = 10,
.tCS = 0,
......@@ -296,9 +303,23 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
#define NDTR1_tWHR(c) (min((c), 15) << 4)
#define NDTR1_tAR(c) (min((c), 15) << 0)
#define tCH_NDTR0(r) (((r) >> 19) & 0x7)
#define tCS_NDTR0(r) (((r) >> 16) & 0x7)
#define tWH_NDTR0(r) (((r) >> 11) & 0x7)
#define tWP_NDTR0(r) (((r) >> 8) & 0x7)
#define tRH_NDTR0(r) (((r) >> 3) & 0x7)
#define tRP_NDTR0(r) (((r) >> 0) & 0x7)
#define tR_NDTR1(r) (((r) >> 16) & 0xffff)
#define tWHR_NDTR1(r) (((r) >> 4) & 0xf)
#define tAR_NDTR1(r) (((r) >> 0) & 0xf)
/* convert nano-seconds to nand flash controller clock cycles */
#define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1)
/* convert nand flash controller clock cycles to nano-seconds */
#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_timing *t)
{
......@@ -920,6 +941,82 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
return 0;
}
static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
struct pxa3xx_nand_timing *t)
{
unsigned long nand_clk = clk_get_rate(info->clk);
uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
}
static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
{
uint32_t ndcr = nand_readl(info, NDCR);
struct nand_flash_dev *type = NULL;
uint32_t id = -1;
int i;
default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
if (default_flash.page_size == 2048)
default_flash.cmdset = &largepage_cmdset;
else
default_flash.cmdset = &smallpage_cmdset;
/* set info fields needed to __readid */
info->flash_info = &default_flash;
info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
info->reg_ndcr = ndcr;
if (__readid(info, &id))
return -ENODEV;
/* Lookup the flash id */
id = (id >> 8) & 0xff; /* device id is byte 2 */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (id == nand_flash_ids[i].id) {
type = &nand_flash_ids[i];
break;
}
}
if (!type)
return -ENODEV;
/* fill the missing flash information */
i = __ffs(default_flash.page_per_block * default_flash.page_size);
default_flash.num_blocks = type->chipsize << (20 - i);
info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
/* calculate addressing information */
info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
if (default_flash.num_blocks * default_flash.page_per_block > 65536)
info->row_addr_cycles = 3;
else
info->row_addr_cycles = 2;
pxa3xx_nand_detect_timing(info, &default_timing);
default_flash.timing = &default_timing;
return 0;
}
static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
const struct pxa3xx_nand_platform_data *pdata)
{
......@@ -927,6 +1024,10 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
uint32_t id = -1;
int i;
if (pdata->keep_config)
if (pxa3xx_nand_detect_config(info) == 0)
return 0;
for (i = 0; i<pdata->num_flash; ++i) {
f = pdata->flash + i;
......@@ -1078,6 +1179,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
this = &info->nand_chip;
mtd->priv = info;
mtd->owner = THIS_MODULE;
info->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(info->clk)) {
......@@ -1117,14 +1219,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
goto fail_put_clk;
}
r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
r = request_mem_region(r->start, resource_size(r), pdev->name);
if (r == NULL) {
dev_err(&pdev->dev, "failed to request memory resource\n");
ret = -EBUSY;
goto fail_put_clk;
}
info->mmio_base = ioremap(r->start, r->end - r->start + 1);
info->mmio_base = ioremap(r->start, resource_size(r));
if (info->mmio_base == NULL) {
dev_err(&pdev->dev, "ioremap() failed\n");
ret = -ENODEV;
......@@ -1173,7 +1275,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
fail_free_io:
iounmap(info->mmio_base);
fail_free_res:
release_mem_region(r->start, r->end - r->start + 1);
release_mem_region(r->start, resource_size(r));
fail_put_clk:
clk_disable(info->clk);
clk_put(info->clk);
......@@ -1186,6 +1288,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
struct pxa3xx_nand_info *info = mtd->priv;
struct resource *r;
platform_set_drvdata(pdev, NULL);
......@@ -1198,6 +1301,14 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
info->data_buff, info->data_buff_phys);
} else
kfree(info->data_buff);
iounmap(info->mmio_base);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(r->start, resource_size(r));
clk_disable(info->clk);
clk_put(info->clk);
kfree(mtd);
return 0;
}
......
......@@ -58,7 +58,7 @@ static struct nand_bbt_descr flctl_4secc_smallpage = {
};
static struct nand_bbt_descr flctl_4secc_largepage = {
.options = 0,
.options = NAND_BBT_SCAN2NDPAGE,
.offs = 58,
.len = 2,
.pattern = scan_ff_pattern,
......@@ -149,7 +149,7 @@ static void wait_wfifo_ready(struct sh_flctl *flctl)
printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n");
}
static int wait_recfifo_ready(struct sh_flctl *flctl)
static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
{
uint32_t timeout = LOOP_TIMEOUT_MAX;
int checked[4];
......@@ -183,7 +183,12 @@ static int wait_recfifo_ready(struct sh_flctl *flctl)
uint8_t org;
int index;
index = data >> 16;
if (flctl->page_size)
index = (512 * sector_number) +
(data >> 16);
else
index = data >> 16;
org = flctl->done_buff[index];
flctl->done_buff[index] = org ^ (data & 0xFF);
checked[i] = 1;
......@@ -238,14 +243,14 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
}
}
static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff)
static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector)
{
int i;
unsigned long *ecc_buf = (unsigned long *)buff;
void *fifo_addr = (void *)FLECFIFO(flctl);
for (i = 0; i < 4; i++) {
if (wait_recfifo_ready(flctl))
if (wait_recfifo_ready(flctl , sector))
return 1;
ecc_buf[i] = readl(fifo_addr);
ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
......@@ -384,7 +389,8 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
read_fiforeg(flctl, 512, 512 * sector);
ret = read_ecfiforeg(flctl,
&flctl->done_buff[mtd->writesize + 16 * sector]);
&flctl->done_buff[mtd->writesize + 16 * sector],
sector);
if (ret)
flctl->hwecc_cant_correct[sector] = 1;
......
/*
* drivers/mtd/nand/socrates_nand.c
*
* Copyright © 2008 Ilya Yanok, Emcraft Systems
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/of_platform.h>
#include <linux/io.h>
#define FPGA_NAND_CMD_MASK (0x7 << 28)
#define FPGA_NAND_CMD_COMMAND (0x0 << 28)
#define FPGA_NAND_CMD_ADDR (0x1 << 28)
#define FPGA_NAND_CMD_READ (0x2 << 28)
#define FPGA_NAND_CMD_WRITE (0x3 << 28)
#define FPGA_NAND_BUSY (0x1 << 15)
#define FPGA_NAND_ENABLE (0x1 << 31)
#define FPGA_NAND_DATA_SHIFT 16
struct socrates_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
struct device *dev;
};
/**
* socrates_nand_write_buf - write buffer to chip
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*/
static void socrates_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
struct socrates_nand_host *host = this->priv;
for (i = 0; i < len; i++) {
out_be32(host->io_base, FPGA_NAND_ENABLE |
FPGA_NAND_CMD_WRITE |
(buf[i] << FPGA_NAND_DATA_SHIFT));
}
}
/**
* socrates_nand_read_buf - read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*/
static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
struct socrates_nand_host *host = this->priv;
uint32_t val;
val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ;
out_be32(host->io_base, val);
for (i = 0; i < len; i++) {
buf[i] = (in_be32(host->io_base) >>
FPGA_NAND_DATA_SHIFT) & 0xff;
}
}
/**
* socrates_nand_read_byte - read one byte from the chip
* @mtd: MTD device structure
*/
static uint8_t socrates_nand_read_byte(struct mtd_info *mtd)
{
uint8_t byte;
socrates_nand_read_buf(mtd, &byte, sizeof(byte));
return byte;
}
/**
* socrates_nand_read_word - read one word from the chip
* @mtd: MTD device structure
*/
static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
{
uint16_t word;
socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word));
return word;
}
/**
* socrates_nand_verify_buf - Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*/
static int socrates_nand_verify_buf(struct mtd_info *mtd, const u8 *buf,
int len)
{
int i;
for (i = 0; i < len; i++) {
if (buf[i] != socrates_nand_read_byte(mtd))
return -EFAULT;
}
return 0;
}
/*
* Hardware specific access to control-lines
*/
static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
struct nand_chip *nand_chip = mtd->priv;
struct socrates_nand_host *host = nand_chip->priv;
uint32_t val;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
val = FPGA_NAND_CMD_COMMAND;
else
val = FPGA_NAND_CMD_ADDR;
if (ctrl & NAND_NCE)
val |= FPGA_NAND_ENABLE;
val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT;
out_be32(host->io_base, val);
}
/*
* Read the Device Ready pin.
*/
static int socrates_nand_device_ready(struct mtd_info *mtd)
{
struct nand_chip *nand_chip = mtd->priv;
struct socrates_nand_host *host = nand_chip->priv;
if (in_be32(host->io_base) & FPGA_NAND_BUSY)
return 0; /* busy */
return 1;
}
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/*
* Probe for the NAND device.
*/
static int __devinit socrates_nand_probe(struct of_device *ofdev,
const struct of_device_id *ofid)
{
struct socrates_nand_host *host;
struct mtd_info *mtd;
struct nand_chip *nand_chip;
int res;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *partitions = NULL;
int num_partitions = 0;
#endif
/* Allocate memory for the device structure (and zero it) */
host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL);
if (!host) {
printk(KERN_ERR
"socrates_nand: failed to allocate device structure.\n");
return -ENOMEM;
}
host->io_base = of_iomap(ofdev->node, 0);
if (host->io_base == NULL) {
printk(KERN_ERR "socrates_nand: ioremap failed\n");
kfree(host);
return -EIO;
}
mtd = &host->mtd;
nand_chip = &host->nand_chip;
host->dev = &ofdev->dev;
nand_chip->priv = host; /* link the private data structures */
mtd->priv = nand_chip;
mtd->name = "socrates_nand";
mtd->owner = THIS_MODULE;
mtd->dev.parent = &ofdev->dev;
/*should never be accessed directly */
nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
nand_chip->IO_ADDR_W = (void *)0xdeadbeef;
nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl;
nand_chip->read_byte = socrates_nand_read_byte;
nand_chip->read_word = socrates_nand_read_word;
nand_chip->write_buf = socrates_nand_write_buf;
nand_chip->read_buf = socrates_nand_read_buf;
nand_chip->verify_buf = socrates_nand_verify_buf;
nand_chip->dev_ready = socrates_nand_device_ready;
nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
/* TODO: I have no idea what real delay is. */
nand_chip->chip_delay = 20; /* 20us command delay time */
dev_set_drvdata(&ofdev->dev, host);
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1)) {
res = -ENXIO;
goto out;
}
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
goto out;
}
#ifdef CONFIG_MTD_PARTITIONS
#ifdef CONFIG_MTD_CMDLINE_PARTS
num_partitions = parse_mtd_partitions(mtd, part_probes,
&partitions, 0);
if (num_partitions < 0) {
res = num_partitions;
goto release;
}
#endif
#ifdef CONFIG_MTD_OF_PARTS
if (num_partitions == 0) {
num_partitions = of_mtd_parse_partitions(&ofdev->dev,
ofdev->node,
&partitions);
if (num_partitions < 0) {
res = num_partitions;
goto release;
}
}
#endif
if (partitions && (num_partitions > 0))
res = add_mtd_partitions(mtd, partitions, num_partitions);
else
#endif
res = add_mtd_device(mtd);
if (!res)
return res;
#ifdef CONFIG_MTD_PARTITIONS
release:
#endif
nand_release(mtd);
out:
dev_set_drvdata(&ofdev->dev, NULL);
iounmap(host->io_base);
kfree(host);
return res;
}
/*
* Remove a NAND device.
*/
static int __devexit socrates_nand_remove(struct of_device *ofdev)
{
struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
struct mtd_info *mtd = &host->mtd;
nand_release(mtd);
dev_set_drvdata(&ofdev->dev, NULL);
iounmap(host->io_base);
kfree(host);
return 0;
}
static struct of_device_id socrates_nand_match[] =
{
{
.compatible = "abb,socrates-nand",
},
{},
};
MODULE_DEVICE_TABLE(of, socrates_nand_match);
static struct of_platform_driver socrates_nand_driver = {
.name = "socrates_nand",
.match_table = socrates_nand_match,
.probe = socrates_nand_probe,
.remove = __devexit_p(socrates_nand_remove),
};
static int __init socrates_nand_init(void)
{
return of_register_platform_driver(&socrates_nand_driver);
}
static void __exit socrates_nand_exit(void)
{
of_unregister_platform_driver(&socrates_nand_driver);
}
module_init(socrates_nand_init);
module_exit(socrates_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ilya Yanok");
MODULE_DESCRIPTION("NAND driver for Socrates board");
This diff is collapsed.
......@@ -15,11 +15,11 @@
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/hdreg.h>
#include <linux/blkdev.h>
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
......@@ -818,3 +818,4 @@ module_exit(cleanup_nftl);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
......@@ -46,6 +46,13 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
const u32 *reg;
int len;
/* check if this is a partition node */
partname = of_get_property(pp, "name", &len);
if (strcmp(partname, "partition") != 0) {
nr_parts--;
continue;
}
reg = of_get_property(pp, "reg", &len);
if (!reg || (len != 2 * sizeof(u32))) {
of_node_put(pp);
......
......@@ -294,6 +294,10 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,
if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
goto out_copy;
/* panic_write() may be in an interrupt context */
if (in_interrupt())
goto out_copy;
if (buf >= high_memory) {
struct page *p1;
......@@ -672,6 +676,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
c->mtd.priv = &c->onenand;
c->mtd.owner = THIS_MODULE;
c->mtd.dev.parent = &pdev->dev;
if (c->dma_channel >= 0) {
struct onenand_chip *this = &c->onenand;
......
......@@ -1455,7 +1455,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
struct onenand_chip *this = mtd->priv;
int written = 0, column, thislen, subpage;
int written = 0, column, thislen = 0, subpage = 0;
int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
int oobwritten = 0, oobcolumn, thisooblen, oobsize;
size_t len = ops->len;
size_t ooblen = ops->ooblen;
......@@ -1482,6 +1483,10 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
return -EINVAL;
}
/* Check zero length */
if (!len)
return 0;
if (ops->mode == MTD_OOB_AUTO)
oobsize = this->ecclayout->oobavail;
else
......@@ -1492,79 +1497,121 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
column = to & (mtd->writesize - 1);
/* Loop until all data write */
while (written < len) {
u_char *wbuf = (u_char *) buf;
while (1) {
if (written < len) {
u_char *wbuf = (u_char *) buf;
thislen = min_t(int, mtd->writesize - column, len - written);
thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
thislen = min_t(int, mtd->writesize - column, len - written);
thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
cond_resched();
cond_resched();
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
/* Partial page write */
subpage = thislen < mtd->writesize;
if (subpage) {
memset(this->page_buf, 0xff, mtd->writesize);
memcpy(this->page_buf + column, buf, thislen);
wbuf = this->page_buf;
}
/* Partial page write */
subpage = thislen < mtd->writesize;
if (subpage) {
memset(this->page_buf, 0xff, mtd->writesize);
memcpy(this->page_buf + column, buf, thislen);
wbuf = this->page_buf;
}
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
if (oob) {
oobbuf = this->oob_buf;
if (oob) {
oobbuf = this->oob_buf;
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
if (ops->mode == MTD_OOB_AUTO)
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob, thisooblen);
/* We send data to spare ram with oobsize
* to prevent byte access */
memset(oobbuf, 0xff, mtd->oobsize);
if (ops->mode == MTD_OOB_AUTO)
onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
else
memcpy(oobbuf + oobcolumn, oob, thisooblen);
oobwritten += thisooblen;
oob += thisooblen;
oobcolumn = 0;
oobwritten += thisooblen;
oob += thisooblen;
oobcolumn = 0;
} else
oobbuf = (u_char *) ffchars;
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
} else
oobbuf = (u_char *) ffchars;
ONENAND_SET_NEXT_BUFFERRAM(this);
this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
/*
* 2 PLANE, MLC, and Flex-OneNAND doesn't support
* write-while-programe feature.
*/
if (!ONENAND_IS_2PLANE(this) && !first) {
ONENAND_SET_PREV_BUFFERRAM(this);
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
ret = this->wait(mtd, FL_WRITING);
ret = this->wait(mtd, FL_WRITING);
/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
if (ret) {
written -= prevlen;
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
break;
}
/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, to, !ret && !subpage);
if (ONENAND_IS_2PLANE(this)) {
ONENAND_SET_BUFFERRAM1(this);
onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
}
if (written == len) {
/* Only check verify write turn on */
ret = onenand_verify(mtd, buf - len, to - len, len);
if (ret)
printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
break;
}
if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
break;
ONENAND_SET_NEXT_BUFFERRAM(this);
}
/* Only check verify write turn on */
ret = onenand_verify(mtd, buf, to, thislen);
if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
break;
}
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
written += thislen;
/*
* 2 PLANE, MLC, and Flex-OneNAND wait here
*/
if (ONENAND_IS_2PLANE(this)) {
ret = this->wait(mtd, FL_WRITING);
if (written == len)
break;
/* In partial page write we don't update bufferram */
onenand_update_bufferram(mtd, to, !ret && !subpage);
if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
break;
}
/* Only check verify write turn on */
ret = onenand_verify(mtd, buf, to, thislen);
if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
break;
}
written += thislen;
if (written == len)
break;
} else
written += thislen;
column = 0;
prev_subpage = subpage;
prev = to;
prevlen = thislen;
to += thislen;
buf += thislen;
first = 0;
}
/* In error case, clear all bufferrams */
if (written != len)
onenand_invalidate_bufferram(mtd, 0, -1);
ops->retlen = written;
ops->oobretlen = oobwritten;
return ret;
}
......
......@@ -38,12 +38,12 @@ static int jffs2_acl_count(size_t size)
size_t s;
size -= sizeof(struct jffs2_acl_header);
s = size - 4 * sizeof(struct jffs2_acl_entry_short);
if (s < 0) {
if (size < 4 * sizeof(struct jffs2_acl_entry_short)) {
if (size % sizeof(struct jffs2_acl_entry_short))
return -1;
return size / sizeof(struct jffs2_acl_entry_short);
} else {
s = size - 4 * sizeof(struct jffs2_acl_entry_short);
if (s % sizeof(struct jffs2_acl_entry))
return -1;
return s / sizeof(struct jffs2_acl_entry) + 4;
......
......@@ -284,10 +284,9 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
{
struct jffs2_xattr_datum *xd;
xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
xd = kmem_cache_zalloc(xattr_datum_cache, GFP_KERNEL);
dbg_memalloc("%p\n", xd);
memset(xd, 0, sizeof(struct jffs2_xattr_datum));
xd->class = RAWNODE_CLASS_XATTR_DATUM;
xd->node = (void *)xd;
INIT_LIST_HEAD(&xd->xindex);
......@@ -303,10 +302,9 @@ void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
{
struct jffs2_xattr_ref *ref;
ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
ref = kmem_cache_zalloc(xattr_ref_cache, GFP_KERNEL);
dbg_memalloc("%p\n", ref);
memset(ref, 0, sizeof(struct jffs2_xattr_ref));
ref->class = RAWNODE_CLASS_XATTR_REF;
ref->node = (void *)ref;
return ref;
......
config ROMFS_FS
tristate "ROM file system support"
depends on BLOCK
depends on BLOCK || MTD
---help---
This is a very small read-only file system mainly intended for
initial ram disks of installation disks, but it could be used for
......@@ -14,3 +14,49 @@ config ROMFS_FS
If you don't know whether you need it, then you don't need it:
answer N.
#
# Select the backing stores to be supported
#
choice
prompt "RomFS backing stores"
depends on ROMFS_FS
default ROMFS_BACKED_BY_BLOCK
help
Select the backing stores to be supported.
config ROMFS_BACKED_BY_BLOCK
bool "Block device-backed ROM file system support"
depends on BLOCK
help
This permits ROMFS to use block devices buffered through the page
cache as the medium from which to retrieve data. It does not allow
direct mapping of the medium.
If unsure, answer Y.
config ROMFS_BACKED_BY_MTD
bool "MTD-backed ROM file system support"
depends on MTD=y || (ROMFS_FS=m && MTD)
help
This permits ROMFS to use MTD based devices directly, without the
intercession of the block layer (which may have been disabled). It
also allows direct mapping of MTD devices through romfs files under
NOMMU conditions if the underlying device is directly addressable by
the CPU.
If unsure, answer Y.
config ROMFS_BACKED_BY_BOTH
bool "Both the above"
depends on BLOCK && (MTD=y || (ROMFS_FS=m && MTD))
endchoice
config ROMFS_ON_BLOCK
bool
default y if ROMFS_BACKED_BY_BLOCK || ROMFS_BACKED_BY_BOTH
config ROMFS_ON_MTD
bool
default y if ROMFS_BACKED_BY_MTD || ROMFS_BACKED_BY_BOTH
#
# Makefile for the linux romfs filesystem routines.
# Makefile for the linux RomFS filesystem routines.
#
obj-$(CONFIG_ROMFS_FS) += romfs.o
romfs-objs := inode.o
romfs-y := storage.o super.o
ifneq ($(CONFIG_MMU),y)
romfs-$(CONFIG_ROMFS_ON_MTD) += mmap-nommu.o
endif
/* RomFS internal definitions
*
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/romfs_fs.h>
struct romfs_inode_info {
struct inode vfs_inode;
unsigned long i_metasize; /* size of non-data area */
unsigned long i_dataoffset; /* from the start of fs */
};
static inline size_t romfs_maxsize(struct super_block *sb)
{
return (size_t) (unsigned long) sb->s_fs_info;
}
static inline struct romfs_inode_info *ROMFS_I(struct inode *inode)
{
return container_of(inode, struct romfs_inode_info, vfs_inode);
}
/*
* mmap-nommu.c
*/
#if !defined(CONFIG_MMU) && defined(CONFIG_ROMFS_ON_MTD)
extern const struct file_operations romfs_ro_fops;
#else
#define romfs_ro_fops generic_ro_fops
#endif
/*
* storage.c
*/
extern int romfs_dev_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen);
extern ssize_t romfs_dev_strnlen(struct super_block *sb,
unsigned long pos, size_t maxlen);
extern int romfs_dev_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size);
/* NOMMU mmap support for RomFS on MTD devices
*
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/mm.h>
#include <linux/mtd/super.h>
#include "internal.h"
/*
* try to determine where a shared mapping can be made
* - only supported for NOMMU at the moment (MMU can't doesn't copy private
* mappings)
* - attempts to map through to the underlying MTD device
*/
static unsigned long romfs_get_unmapped_area(struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
unsigned long flags)
{
struct inode *inode = file->f_mapping->host;
struct mtd_info *mtd = inode->i_sb->s_mtd;
unsigned long isize, offset;
if (!mtd)
goto cant_map_directly;
isize = i_size_read(inode);
offset = pgoff << PAGE_SHIFT;
if (offset > isize || len > isize || offset > isize - len)
return (unsigned long) -EINVAL;
/* we need to call down to the MTD layer to do the actual mapping */
if (mtd->get_unmapped_area) {
if (addr != 0)
return (unsigned long) -EINVAL;
if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
return (unsigned long) -EINVAL;
offset += ROMFS_I(inode)->i_dataoffset;
if (offset > mtd->size - len)
return (unsigned long) -EINVAL;
return mtd->get_unmapped_area(mtd, len, offset, flags);
}
cant_map_directly:
return (unsigned long) -ENOSYS;
}
/*
* permit a R/O mapping to be made directly through onto an MTD device if
* possible
*/
static int romfs_mmap(struct file *file, struct vm_area_struct *vma)
{
return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS;
}
const struct file_operations romfs_ro_fops = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
.splice_read = generic_file_splice_read,
.mmap = romfs_mmap,
.get_unmapped_area = romfs_get_unmapped_area,
};
/* RomFS storage access routines
*
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.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 the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/fs.h>
#include <linux/mtd/super.h>
#include <linux/buffer_head.h>
#include "internal.h"
#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)
#error no ROMFS backing store interface configured
#endif
#ifdef CONFIG_ROMFS_ON_MTD
#define ROMFS_MTD_READ(sb, ...) ((sb)->s_mtd->read((sb)->s_mtd, ##__VA_ARGS__))
/*
* read data from an romfs image on an MTD device
*/
static int romfs_mtd_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen)
{
size_t rlen;
int ret;
ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf);
return (ret < 0 || rlen != buflen) ? -EIO : 0;
}
/*
* determine the length of a string in a romfs image on an MTD device
*/
static ssize_t romfs_mtd_strnlen(struct super_block *sb,
unsigned long pos, size_t maxlen)
{
ssize_t n = 0;
size_t segment;
u_char buf[16], *p;
size_t len;
int ret;
/* scan the string up to 16 bytes at a time */
while (maxlen > 0) {
segment = min_t(size_t, maxlen, 16);
ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
if (ret < 0)
return ret;
p = memchr(buf, 0, len);
if (p)
return n + (p - buf);
maxlen -= len;
pos += len;
n += len;
}
return n;
}
/*
* compare a string to one in a romfs image on MTD
* - return 1 if matched, 0 if differ, -ve if error
*/
static int romfs_mtd_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
{
u_char buf[16];
size_t len, segment;
int ret;
/* scan the string up to 16 bytes at a time */
while (size > 0) {
segment = min_t(size_t, size, 16);
ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
if (ret < 0)
return ret;
if (memcmp(buf, str, len) != 0)
return 0;
size -= len;
pos += len;
str += len;
}
return 1;
}
#endif /* CONFIG_ROMFS_ON_MTD */
#ifdef CONFIG_ROMFS_ON_BLOCK
/*
* read data from an romfs image on a block device
*/
static int romfs_blk_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen)
{
struct buffer_head *bh;
unsigned long offset;
size_t segment;
/* copy the string up to blocksize bytes at a time */
while (buflen > 0) {
offset = pos & (ROMBSIZE - 1);
segment = min_t(size_t, buflen, ROMBSIZE - offset);
bh = sb_bread(sb, pos >> ROMBSBITS);
if (!bh)
return -EIO;
memcpy(buf, bh->b_data + offset, segment);
brelse(bh);
buflen -= segment;
pos += segment;
}
return 0;
}
/*
* determine the length of a string in romfs on a block device
*/
static ssize_t romfs_blk_strnlen(struct super_block *sb,
unsigned long pos, size_t limit)
{
struct buffer_head *bh;
unsigned long offset;
ssize_t n = 0;
size_t segment;
u_char *buf, *p;
/* scan the string up to blocksize bytes at a time */
while (limit > 0) {
offset = pos & (ROMBSIZE - 1);
segment = min_t(size_t, limit, ROMBSIZE - offset);
bh = sb_bread(sb, pos >> ROMBSBITS);
if (!bh)
return -EIO;
buf = bh->b_data + offset;
p = memchr(buf, 0, segment);
brelse(bh);
if (p)
return n + (p - buf);
limit -= segment;
pos += segment;
n += segment;
}
return n;
}
/*
* compare a string to one in a romfs image on a block device
* - return 1 if matched, 0 if differ, -ve if error
*/
static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
{
struct buffer_head *bh;
unsigned long offset;
size_t segment;
bool x;
/* scan the string up to 16 bytes at a time */
while (size > 0) {
offset = pos & (ROMBSIZE - 1);
segment = min_t(size_t, size, ROMBSIZE - offset);
bh = sb_bread(sb, pos >> ROMBSBITS);
if (!bh)
return -EIO;
x = (memcmp(bh->b_data + offset, str, segment) != 0);
brelse(bh);
if (x)
return 0;
size -= segment;
pos += segment;
str += segment;
}
return 1;
}
#endif /* CONFIG_ROMFS_ON_BLOCK */
/*
* read data from the romfs image
*/
int romfs_dev_read(struct super_block *sb, unsigned long pos,
void *buf, size_t buflen)
{
size_t limit;
limit = romfs_maxsize(sb);
if (pos >= limit)
return -EIO;
if (buflen > limit - pos)
buflen = limit - pos;
#ifdef CONFIG_ROMFS_ON_MTD
if (sb->s_mtd)
return romfs_mtd_read(sb, pos, buf, buflen);
#endif
#ifdef CONFIG_ROMFS_ON_BLOCK
if (sb->s_bdev)
return romfs_blk_read(sb, pos, buf, buflen);
#endif
return -EIO;
}
/*
* determine the length of a string in romfs
*/
ssize_t romfs_dev_strnlen(struct super_block *sb,
unsigned long pos, size_t maxlen)
{
size_t limit;
limit = romfs_maxsize(sb);
if (pos >= limit)
return -EIO;
if (maxlen > limit - pos)
maxlen = limit - pos;
#ifdef CONFIG_ROMFS_ON_MTD
if (sb->s_mtd)
return romfs_mtd_strnlen(sb, pos, limit);
#endif
#ifdef CONFIG_ROMFS_ON_BLOCK
if (sb->s_bdev)
return romfs_blk_strnlen(sb, pos, limit);
#endif
return -EIO;
}
/*
* compare a string to one in romfs
* - return 1 if matched, 0 if differ, -ve if error
*/
int romfs_dev_strncmp(struct super_block *sb, unsigned long pos,
const char *str, size_t size)
{
size_t limit;
limit = romfs_maxsize(sb);
if (pos >= limit)
return -EIO;
if (size > ROMFS_MAXFN)
return -ENAMETOOLONG;
if (size > limit - pos)
return -EIO;
#ifdef CONFIG_ROMFS_ON_MTD
if (sb->s_mtd)
return romfs_mtd_strncmp(sb, pos, str, size);
#endif
#ifdef CONFIG_ROMFS_ON_BLOCK
if (sb->s_bdev)
return romfs_blk_strncmp(sb, pos, str, size);
#endif
return -EIO;
}
This diff is collapsed.
......@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/uio.h>
#include <linux/notifier.h>
#include <linux/device.h>
#include <linux/mtd/compatmac.h>
#include <mtd/mtd-abi.h>
......@@ -162,6 +163,20 @@ struct mtd_info {
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
/* Allow NOMMU mmap() to directly map the device (if not NULL)
* - return the address to which the offset maps
* - return -ENOSYS to indicate refusal to do the mapping
*/
unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags);
/* Backing device capabilities for this device
* - provides mmap capabilities
*/
struct backing_dev_info *backing_dev_info;
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
......@@ -223,6 +238,7 @@ struct mtd_info {
void *priv;
struct module *owner;
struct device dev;
int usecount;
/* If the driver is something smart, like UBI, it may need to maintain
......@@ -233,6 +249,11 @@ struct mtd_info {
void (*put_device) (struct mtd_info *mtd);
};
static inline struct mtd_info *dev_to_mtd(struct device *dev)
{
return dev ? container_of(dev, struct mtd_info, dev) : NULL;
}
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
{
if (mtd->erasesize_shift)
......
......@@ -43,8 +43,8 @@ extern void nand_wait_ready(struct mtd_info *mtd);
* is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly.
*/
#define NAND_MAX_OOBSIZE 64
#define NAND_MAX_PAGESIZE 2048
#define NAND_MAX_OOBSIZE 128
#define NAND_MAX_PAGESIZE 4096
/*
* Constants for hardware specific CLE/ALE/NCE function
......
......@@ -76,4 +76,16 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
struct device_node *node,
struct mtd_partition **pparts);
#ifdef CONFIG_MTD_PARTITIONS
static inline int mtd_has_partitions(void) { return 1; }
#else
static inline int mtd_has_partitions(void) { return 0; }
#endif
#ifdef CONFIG_MTD_CMDLINE_PARTS
static inline int mtd_has_cmdlinepart(void) { return 1; }
#else
static inline int mtd_has_cmdlinepart(void) { return 0; }
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment