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
This commit is contained in:
commit
22ae77bc7a
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
80
arch/arm/mach-davinci/include/mach/nand.h
Normal file
80
arch/arm/mach-davinci/include/mach/nand.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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;
|
||||
|
30
arch/mips/include/asm/txx9/ndfmc.h
Normal file
30
arch/mips/include/asm/txx9/ndfmc.h
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 @@
|
||||
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 @@
|
||||
|
||||
partition@0 {
|
||||
label = "fs";
|
||||
reg = <0x00000000 0x01000000>;
|
||||
reg = <0x00000000 0x10000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -397,10 +397,13 @@
|
||||
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 @@
|
||||
|
||||
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>
|
||||
|
17
drivers/mtd/internal.h
Normal file
17
drivers/mtd/internal.h
Normal file
@ -0,0 +1,17 @@
|
||||
/* 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);
|
||||
|
||||
|
208
drivers/mtd/maps/rbtx4939-flash.c
Normal file
208
drivers/mtd/maps/rbtx4939-flash.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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;
|
||||
|
@ -1,116 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
43
drivers/mtd/mtdbdi.c
Normal file
43
drivers/mtd/mtdbdi.c
Normal file
@ -0,0 +1,43 @@
|
||||
/* 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(¬ifier);
|
||||
return 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mtdchar(void)
|
||||
{
|
||||
unregister_mtd_user(¬ifier);
|
||||
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 @@ done:
|
||||
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 @@ out_register:
|
||||
* 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;
|
||||
|
||||
|
570
drivers/mtd/nand/davinci_nand.c
Normal file
570
drivers/mtd/nand/davinci_nand.c
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
* davinci_nand.c - NAND Flash Driver for DaVinci family chips
|
||||
*
|
||||
* Copyright © 2006 Texas Instruments.
|
||||
*
|
||||
* Port 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <mach/nand.h>
|
||||
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
|
||||
/*
|
||||
* This is a device driver for the NAND flash controller found on the
|
||||
* various DaVinci family chips. It handles up to four SoC chipselects,
|
||||
* and some flavors of secondary chipselect (e.g. based on A12) as used
|
||||
* with multichip packages.
|
||||
*
|
||||
* The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC
|
||||
* available on chips like the DM355 and OMAP-L137 and needed with the
|
||||
* more error-prone MLC NAND chips.
|
||||
*
|
||||
* This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY
|
||||
* outputs in a "wire-AND" configuration, with no per-chip signals.
|
||||
*/
|
||||
struct davinci_nand_info {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
|
||||
struct device *dev;
|
||||
struct clk *clk;
|
||||
bool partitioned;
|
||||
|
||||
void __iomem *base;
|
||||
void __iomem *vaddr;
|
||||
|
||||
uint32_t ioaddr;
|
||||
uint32_t current_cs;
|
||||
|
||||
uint32_t mask_chipsel;
|
||||
uint32_t mask_ale;
|
||||
uint32_t mask_cle;
|
||||
|
||||
uint32_t core_chipsel;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(davinci_nand_lock);
|
||||
|
||||
#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
|
||||
|
||||
|
||||
static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
|
||||
int offset)
|
||||
{
|
||||
return __raw_readl(info->base + offset);
|
||||
}
|
||||
|
||||
static inline void davinci_nand_writel(struct davinci_nand_info *info,
|
||||
int offset, unsigned long value)
|
||||
{
|
||||
__raw_writel(value, info->base + offset);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Access to hardware control lines: ALE, CLE, secondary chipselect.
|
||||
*/
|
||||
|
||||
static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
uint32_t addr = info->current_cs;
|
||||
struct nand_chip *nand = mtd->priv;
|
||||
|
||||
/* Did the control lines change? */
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE)
|
||||
addr |= info->mask_cle;
|
||||
else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE)
|
||||
addr |= info->mask_ale;
|
||||
|
||||
nand->IO_ADDR_W = (void __iomem __force *)addr;
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
iowrite8(cmd, nand->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
uint32_t addr = info->ioaddr;
|
||||
|
||||
/* maybe kick in a second chipselect */
|
||||
if (chip > 0)
|
||||
addr |= info->mask_chipsel;
|
||||
info->current_cs = addr;
|
||||
|
||||
info->chip.IO_ADDR_W = (void __iomem __force *)addr;
|
||||
info->chip.IO_ADDR_R = info->chip.IO_ADDR_W;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* 1-bit hardware ECC ... context maintained for each core chipselect
|
||||
*/
|
||||
|
||||
static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
|
||||
return davinci_nand_readl(info, NANDF1ECC_OFFSET
|
||||
+ 4 * info->core_chipsel);
|
||||
}
|
||||
|
||||
static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct davinci_nand_info *info;
|
||||
uint32_t nandcfr;
|
||||
unsigned long flags;
|
||||
|
||||
info = to_davinci_nand(mtd);
|
||||
|
||||
/* Reset ECC hardware */
|
||||
nand_davinci_readecc_1bit(mtd);
|
||||
|
||||
spin_lock_irqsave(&davinci_nand_lock, flags);
|
||||
|
||||
/* Restart ECC hardware */
|
||||
nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET);
|
||||
nandcfr |= BIT(8 + info->core_chipsel);
|
||||
davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr);
|
||||
|
||||
spin_unlock_irqrestore(&davinci_nand_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read hardware ECC value and pack into three bytes
|
||||
*/
|
||||
static int nand_davinci_calculate_1bit(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
unsigned int ecc_val = nand_davinci_readecc_1bit(mtd);
|
||||
unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4);
|
||||
|
||||
/* invert so that erased block ecc is correct */
|
||||
ecc24 = ~ecc24;
|
||||
ecc_code[0] = (u_char)(ecc24);
|
||||
ecc_code[1] = (u_char)(ecc24 >> 8);
|
||||
ecc_code[2] = (u_char)(ecc24 >> 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
|
||||
(read_ecc[2] << 16);
|
||||
uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
|
||||
(calc_ecc[2] << 16);
|
||||
uint32_t diff = eccCalc ^ eccNand;
|
||||
|
||||
if (diff) {
|
||||
if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
|
||||
/* Correctable error */
|
||||
if ((diff >> (12 + 3)) < chip->ecc.size) {
|
||||
dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7);
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else if (!(diff & (diff - 1))) {
|
||||
/* Single bit ECC error in the ECC itself,
|
||||
* nothing to fix */
|
||||
return 1;
|
||||
} else {
|
||||
/* Uncorrectable error */
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
|
||||
* how these chips are normally wired. This translates to both 8 and 16
|
||||
* bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4).
|
||||
*
|
||||
* For now we assume that configuration, or any other one which ignores
|
||||
* the two LSBs for NAND access ... so we can issue 32-bit reads/writes
|
||||
* and have that transparently morphed into multiple NAND operations.
|
||||
*/
|
||||
static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
|
||||
ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
|
||||
else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
|
||||
ioread16_rep(chip->IO_ADDR_R, buf, len >> 1);
|
||||
else
|
||||
ioread8_rep(chip->IO_ADDR_R, buf, len);
|
||||
}
|
||||
|
||||
static void nand_davinci_write_buf(struct mtd_info *mtd,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
|
||||
iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
|
||||
else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
|
||||
iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1);
|
||||
else
|
||||
iowrite8_rep(chip->IO_ADDR_R, buf, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check hardware register for wait status. Returns 1 if device is ready,
|
||||
* 0 if it is still busy.
|
||||
*/
|
||||
static int nand_davinci_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct davinci_nand_info *info = to_davinci_nand(mtd);
|
||||
|
||||
return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0);
|
||||
}
|
||||
|
||||
static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
|
||||
{
|
||||
uint32_t regval, a1cr;
|
||||
|
||||
/*
|
||||
* NAND FLASH timings @ PLL1 == 459 MHz
|
||||
* - AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz
|
||||
* - AEMIF.CLK period = 1/76.5 MHz = 13.1 ns
|
||||
*/
|
||||
regval = 0
|
||||
| (0 << 31) /* selectStrobe */
|
||||
| (0 << 30) /* extWait (never with NAND) */
|
||||
| (1 << 26) /* writeSetup 10 ns */
|
||||
| (3 << 20) /* writeStrobe 40 ns */
|
||||
| (1 << 17) /* writeHold 10 ns */
|
||||
| (0 << 13) /* readSetup 10 ns */
|
||||
| (3 << 7) /* readStrobe 60 ns */
|
||||
| (0 << 4) /* readHold 10 ns */
|
||||
| (3 << 2) /* turnAround ?? ns */
|
||||
| (0 << 0) /* asyncSize 8-bit bus */
|
||||
;
|
||||
a1cr = davinci_nand_readl(info, A1CR_OFFSET);
|
||||
if (a1cr != regval) {
|
||||
dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \
|
||||
"reg to 0x%08x, was 0x%08x, should be done by " \
|
||||
"bootloader.\n", regval, a1cr);
|
||||
davinci_nand_writel(info, A1CR_OFFSET, regval);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int __init nand_davinci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_nand_pdata *pdata = pdev->dev.platform_data;
|
||||
struct davinci_nand_info *info;
|
||||
struct resource *res1;
|
||||
struct resource *res2;
|
||||
void __iomem *vaddr;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
uint32_t val;
|
||||
nand_ecc_modes_t ecc_mode;
|
||||
|
||||
/* which external chipselect will we be managing? */
|
||||
if (pdev->id < 0 || pdev->id > 3)
|
||||
return -ENODEV;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
dev_err(&pdev->dev, "unable to allocate memory\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res1 || !res2) {
|
||||
dev_err(&pdev->dev, "resource missing\n");
|
||||
ret = -EINVAL;
|
||||
goto err_nomem;
|
||||
}
|
||||
|
||||
vaddr = ioremap(res1->start, res1->end - res1->start);
|
||||
base = ioremap(res2->start, res2->end - res2->start);
|
||||
if (!vaddr || !base) {
|
||||
dev_err(&pdev->dev, "ioremap failed\n");
|
||||
ret = -EINVAL;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->base = base;
|
||||
info->vaddr = vaddr;
|
||||
|
||||
info->mtd.priv = &info->chip;
|
||||
info->mtd.name = dev_name(&pdev->dev);
|
||||
info->mtd.owner = THIS_MODULE;
|
||||
|
||||
info->mtd.dev.parent = &pdev->dev;
|
||||
|
||||
info->chip.IO_ADDR_R = vaddr;
|
||||
info->chip.IO_ADDR_W = vaddr;
|
||||
info->chip.chip_delay = 0;
|
||||
info->chip.select_chip = nand_davinci_select_chip;
|
||||
|
||||
/* options such as NAND_USE_FLASH_BBT or 16-bit widths */
|
||||
info->chip.options = pdata ? pdata->options : 0;
|
||||
|
||||
info->ioaddr = (uint32_t __force) vaddr;
|
||||
|
||||
info->current_cs = info->ioaddr;
|
||||
info->core_chipsel = pdev->id;
|
||||
info->mask_chipsel = pdata->mask_chipsel;
|
||||
|
||||
/* use nandboot-capable ALE/CLE masks by default */
|
||||
if (pdata && pdata->mask_ale)
|
||||
info->mask_ale = pdata->mask_cle;
|
||||
else
|
||||
info->mask_ale = MASK_ALE;
|
||||
if (pdata && pdata->mask_cle)
|
||||
info->mask_cle = pdata->mask_cle;
|
||||
else
|
||||
info->mask_cle = MASK_CLE;
|
||||
|
||||
/* Set address of hardware control function */
|
||||
info->chip.cmd_ctrl = nand_davinci_hwcontrol;
|
||||
info->chip.dev_ready = nand_davinci_dev_ready;
|
||||
|
||||
/* Speed up buffer I/O */
|
||||
info->chip.read_buf = nand_davinci_read_buf;
|
||||
info->chip.write_buf = nand_davinci_write_buf;
|
||||
|
||||
/* use board-specific ECC config; else, the best available */
|
||||
if (pdata)
|
||||
ecc_mode = pdata->ecc_mode;
|
||||
else
|
||||
ecc_mode = NAND_ECC_HW;
|
||||
|
||||
switch (ecc_mode) {
|
||||
case NAND_ECC_NONE:
|
||||
case NAND_ECC_SOFT:
|
||||
break;
|
||||
case NAND_ECC_HW:
|
||||
info->chip.ecc.calculate = nand_davinci_calculate_1bit;
|
||||
info->chip.ecc.correct = nand_davinci_correct_1bit;
|
||||
info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.bytes = 3;
|
||||
break;
|
||||
case NAND_ECC_HW_SYNDROME:
|
||||
/* FIXME implement */
|
||||
info->chip.ecc.size = 512;
|
||||
info->chip.ecc.bytes = 10;
|
||||
|
||||
dev_warn(&pdev->dev, "4-bit ECC nyet supported\n");
|
||||
/* FALL THROUGH */
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto err_ecc;
|
||||
}
|
||||
info->chip.ecc.mode = ecc_mode;
|
||||
|
||||
info->clk = clk_get(&pdev->dev, "AEMIFCLK");
|
||||
if (IS_ERR(info->clk)) {
|
||||
ret = PTR_ERR(info->clk);
|
||||
dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = clk_enable(info->clk);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret);
|
||||
goto err_clk_enable;
|
||||
}
|
||||
|
||||
/* EMIF timings should normally be set by the boot loader,
|
||||
* especially after boot-from-NAND. The *only* reason to
|
||||
* have this special casing for the DM6446 EVM is to work
|
||||
* with boot-from-NOR ... with CS0 manually re-jumpered
|
||||
* (after startup) so it addresses the NAND flash, not NOR.
|
||||
* Even for dev boards, that's unusually rude...
|
||||
*/
|
||||
if (machine_is_davinci_evm())
|
||||
nand_dm6446evm_flash_init(info);
|
||||
|
||||
spin_lock_irq(&davinci_nand_lock);
|
||||
|
||||
/* put CSxNAND into NAND mode */
|
||||
val = davinci_nand_readl(info, NANDFCR_OFFSET);
|
||||
val |= BIT(info->core_chipsel);
|
||||
davinci_nand_writel(info, NANDFCR_OFFSET, val);
|
||||
|
||||
spin_unlock_irq(&davinci_nand_lock);
|
||||
|
||||
/* Scan to find existence of the device(s) */
|
||||
ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1);
|
||||
if (ret < 0) {
|
||||
dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
|
||||
goto err_scan;
|
||||
}
|
||||
|
||||
if (mtd_has_partitions()) {
|
||||
struct mtd_partition *mtd_parts = NULL;
|
||||
int mtd_parts_nb = 0;
|
||||
|
||||
if (mtd_has_cmdlinepart()) {
|
||||
static const char *probes[] __initconst =
|
||||
{ "cmdlinepart", NULL };
|
||||
|
||||
const char *master_name;
|
||||
|
||||
/* Set info->mtd.name = 0 temporarily */
|
||||
master_name = info->mtd.name;
|
||||
info->mtd.name = (char *)0;
|
||||
|
||||
/* info->mtd.name == 0, means: don't bother checking
|
||||
<mtd-id> */
|
||||
mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
|
||||
&mtd_parts, 0);
|
||||
|
||||
/* Restore info->mtd.name */
|
||||
info->mtd.name = master_name;
|
||||
}
|
||||
|
||||
if (mtd_parts_nb <= 0 && pdata) {
|
||||
mtd_parts = pdata->parts;
|
||||
mtd_parts_nb = pdata->nr_parts;
|
||||
}
|
||||
|
||||
/* Register any partitions */
|
||||
if (mtd_parts_nb > 0) {
|
||||
ret = add_mtd_partitions(&info->mtd,
|
||||
mtd_parts, mtd_parts_nb);
|
||||
if (ret == 0)
|
||||
info->partitioned = true;
|
||||
}
|
||||
|
||||
} else if (pdata && pdata->nr_parts) {
|
||||
dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n",
|
||||
pdata->nr_parts, info->mtd.name);
|
||||
}
|
||||
|
||||
/* If there's no partition info, just package the whole chip
|
||||
* as a single MTD device.
|
||||
*/
|
||||
if (!info->partitioned)
|
||||
ret = add_mtd_device(&info->mtd) ? -ENODEV : 0;
|
||||
|
||||
if (ret < 0)
|
||||
goto err_scan;
|
||||
|
||||
val = davinci_nand_readl(info, NRCSR_OFFSET);
|
||||
dev_info(&pdev->dev, "controller rev. %d.%d\n",
|
||||
(val >> 8) & 0xff, val & 0xff);
|
||||
|
||||
return 0;
|
||||
|
||||
err_scan:
|
||||
clk_disable(info->clk);
|
||||
|
||||
err_clk_enable:
|
||||
clk_put(info->clk);
|
||||
|
||||
err_ecc:
|
||||
err_clk:
|
||||
err_ioremap:
|
||||
if (base)
|
||||
iounmap(base);
|
||||
if (vaddr)
|
||||
iounmap(vaddr);
|
||||
|
||||
err_nomem:
|
||||
kfree(info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit nand_davinci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct davinci_nand_info *info = platform_get_drvdata(pdev);
|
||||
int status;
|
||||
|
||||
if (mtd_has_partitions() && info->partitioned)
|
||||
status = del_mtd_partitions(&info->mtd);
|
||||
else
|
||||
status = del_mtd_device(&info->mtd);
|
||||
|
||||
iounmap(info->base);
|
||||
iounmap(info->vaddr);
|
||||
|
||||
nand_release(&info->mtd);
|
||||
|
||||
clk_disable(info->clk);
|
||||
clk_put(info->clk);
|
||||
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver nand_davinci_driver = {
|
||||
.remove = __exit_p(nand_davinci_remove),
|
||||
.driver = {
|
||||
.name = "davinci_nand",
|
||||
},
|
||||
};
|
||||
MODULE_ALIAS("platform:davinci_nand");
|
||||
|
||||
static int __init nand_davinci_init(void)
|
||||
{
|
||||
return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe);
|
||||
}
|
||||
module_init(nand_davinci_init);
|
||||
|
||||
static void __exit nand_davinci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&nand_davinci_driver);
|
||||
}
|
||||
module_exit(nand_davinci_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Texas Instruments");
|
||||
MODULE_DESCRIPTION("Davinci NAND flash driver");
|
||||
|
@ -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);
|
||||
|
||||
fun_wait_rnb(fun);
|
||||
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);
|
||||
|
||||
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 @@ err1:
|
||||
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 @@ fail_free_buf:
|
||||
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;
|
||||
|
325
drivers/mtd/nand/socrates_nand.c
Normal file
325
drivers/mtd/nand/socrates_nand.c
Normal file
@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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");
|
428
drivers/mtd/nand/txx9ndfmc.c
Normal file
428
drivers/mtd/nand/txx9ndfmc.c
Normal file
@ -0,0 +1,428 @@
|
||||
/*
|
||||
* TXx9 NAND flash memory controller driver
|
||||
* Based on RBTX49xx patch from CELF patch archive.
|
||||
*
|
||||
* 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 2004-2007
|
||||
* All Rights Reserved.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/txx9/ndfmc.h>
|
||||
|
||||
/* TXX9 NDFMC Registers */
|
||||
#define TXX9_NDFDTR 0x00
|
||||
#define TXX9_NDFMCR 0x04
|
||||
#define TXX9_NDFSR 0x08
|
||||
#define TXX9_NDFISR 0x0c
|
||||
#define TXX9_NDFIMR 0x10
|
||||
#define TXX9_NDFSPR 0x14
|
||||
#define TXX9_NDFRSTR 0x18 /* not TX4939 */
|
||||
|
||||
/* NDFMCR : NDFMC Mode Control */
|
||||
#define TXX9_NDFMCR_WE 0x80
|
||||
#define TXX9_NDFMCR_ECC_ALL 0x60
|
||||
#define TXX9_NDFMCR_ECC_RESET 0x60
|
||||
#define TXX9_NDFMCR_ECC_READ 0x40
|
||||
#define TXX9_NDFMCR_ECC_ON 0x20
|
||||
#define TXX9_NDFMCR_ECC_OFF 0x00
|
||||
#define TXX9_NDFMCR_CE 0x10
|
||||
#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */
|
||||
#define TXX9_NDFMCR_ALE 0x02
|
||||
#define TXX9_NDFMCR_CLE 0x01
|
||||
/* TX4939 only */
|
||||
#define TXX9_NDFMCR_X16 0x0400
|
||||
#define TXX9_NDFMCR_DMAREQ_MASK 0x0300
|
||||
#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000
|
||||
#define TXX9_NDFMCR_DMAREQ_128 0x0100
|
||||
#define TXX9_NDFMCR_DMAREQ_256 0x0200
|
||||
#define TXX9_NDFMCR_DMAREQ_512 0x0300
|
||||
#define TXX9_NDFMCR_CS_MASK 0x0c
|
||||
#define TXX9_NDFMCR_CS(ch) ((ch) << 2)
|
||||
|
||||
/* NDFMCR : NDFMC Status */
|
||||
#define TXX9_NDFSR_BUSY 0x80
|
||||
/* TX4939 only */
|
||||
#define TXX9_NDFSR_DMARUN 0x40
|
||||
|
||||
/* NDFMCR : NDFMC Reset */
|
||||
#define TXX9_NDFRSTR_RST 0x01
|
||||
|
||||
struct txx9ndfmc_priv {
|
||||
struct platform_device *dev;
|
||||
struct nand_chip chip;
|
||||
struct mtd_info mtd;
|
||||
int cs;
|
||||
char mtdname[BUS_ID_SIZE + 2];
|
||||
};
|
||||
|
||||
#define MAX_TXX9NDFMC_DEV 4
|
||||
struct txx9ndfmc_drvdata {
|
||||
struct mtd_info *mtds[MAX_TXX9NDFMC_DEV];
|
||||
void __iomem *base;
|
||||
unsigned char hold; /* in gbusclock */
|
||||
unsigned char spw; /* in gbusclock */
|
||||
struct nand_hw_control hw_control;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
struct mtd_partition *parts[MAX_TXX9NDFMC_DEV];
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct txx9ndfmc_priv *txx9_priv = chip->priv;
|
||||
return txx9_priv->dev;
|
||||
}
|
||||
|
||||
static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)
|
||||
{
|
||||
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
||||
|
||||
return drvdata->base + (reg << plat->shift);
|
||||
}
|
||||
|
||||
static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg)
|
||||
{
|
||||
return __raw_readl(ndregaddr(dev, reg));
|
||||
}
|
||||
|
||||
static void txx9ndfmc_write(struct platform_device *dev,
|
||||
u32 val, unsigned int reg)
|
||||
{
|
||||
__raw_writel(val, ndregaddr(dev, reg));
|
||||
}
|
||||
|
||||
static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
|
||||
return txx9ndfmc_read(dev, TXX9_NDFDTR);
|
||||
}
|
||||
|
||||
static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
|
||||
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
|
||||
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR);
|
||||
while (len--)
|
||||
__raw_writel(*buf++, ndfdtr);
|
||||
txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);
|
||||
}
|
||||
|
||||
static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
|
||||
|
||||
while (len--)
|
||||
*buf++ = __raw_readl(ndfdtr);
|
||||
}
|
||||
|
||||
static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf,
|
||||
int len)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
|
||||
|
||||
while (len--)
|
||||
if (*buf++ != (uint8_t)__raw_readl(ndfdtr))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
struct txx9ndfmc_priv *txx9_priv = chip->priv;
|
||||
struct platform_device *dev = txx9_priv->dev;
|
||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
|
||||
|
||||
mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE);
|
||||
mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0;
|
||||
mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0;
|
||||
/* TXX9_NDFMCR_CE bit is 0:high 1:low */
|
||||
mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0;
|
||||
if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) {
|
||||
mcr &= ~TXX9_NDFMCR_CS_MASK;
|
||||
mcr |= TXX9_NDFMCR_CS(txx9_priv->cs);
|
||||
}
|
||||
txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR);
|
||||
if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) {
|
||||
/* dummy write to update external latch */
|
||||
if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE)
|
||||
txx9ndfmc_write(dev, 0, TXX9_NDFDTR);
|
||||
}
|
||||
mmiowb();
|
||||
}
|
||||
|
||||
static int txx9ndfmc_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
|
||||
return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY);
|
||||
}
|
||||
|
||||
static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
|
||||
uint8_t *ecc_code)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
|
||||
|
||||
mcr &= ~TXX9_NDFMCR_ECC_ALL;
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR);
|
||||
ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);
|
||||
ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);
|
||||
ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct platform_device *dev = mtd_to_platdev(mtd);
|
||||
u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
|
||||
|
||||
mcr &= ~TXX9_NDFMCR_ECC_ALL;
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR);
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
|
||||
txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR);
|
||||
}
|
||||
|
||||
static void txx9ndfmc_initialize(struct platform_device *dev)
|
||||
{
|
||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
||||
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
||||
int tmout = 100;
|
||||
|
||||
if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR)
|
||||
; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */
|
||||
else {
|
||||
/* reset NDFMC */
|
||||
txx9ndfmc_write(dev,
|
||||
txx9ndfmc_read(dev, TXX9_NDFRSTR) |
|
||||
TXX9_NDFRSTR_RST,
|
||||
TXX9_NDFRSTR);
|
||||
while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) {
|
||||
if (--tmout == 0) {
|
||||
dev_err(&dev->dev, "reset failed.\n");
|
||||
break;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
}
|
||||
/* setup Hold Time, Strobe Pulse Width */
|
||||
txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR);
|
||||
txx9ndfmc_write(dev,
|
||||
(plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ?
|
||||
TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR);
|
||||
}
|
||||
|
||||
#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \
|
||||
DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)
|
||||
|
||||
static int __init txx9ndfmc_probe(struct platform_device *dev)
|
||||
{
|
||||
struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
int hold, spw;
|
||||
int i;
|
||||
struct txx9ndfmc_drvdata *drvdata;
|
||||
unsigned long gbusclk = plat->gbus_clock;
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
if (!devm_request_mem_region(&dev->dev, res->start,
|
||||
resource_size(res), dev_name(&dev->dev)))
|
||||
return -EBUSY;
|
||||
drvdata->base = devm_ioremap(&dev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!drvdata->base)
|
||||
return -EBUSY;
|
||||
|
||||
hold = plat->hold ?: 20; /* tDH */
|
||||
spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */
|
||||
|
||||
hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold);
|
||||
spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw);
|
||||
if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD)
|
||||
hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */
|
||||
spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */
|
||||
hold = clamp(hold, 1, 15);
|
||||
drvdata->hold = hold;
|
||||
spw = clamp(spw, 1, 15);
|
||||
drvdata->spw = spw;
|
||||
dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
|
||||
(gbusclk + 500000) / 1000000, hold, spw);
|
||||
|
||||
spin_lock_init(&drvdata->hw_control.lock);
|
||||
init_waitqueue_head(&drvdata->hw_control.wq);
|
||||
|
||||
platform_set_drvdata(dev, drvdata);
|
||||
txx9ndfmc_initialize(dev);
|
||||
|
||||
for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
|
||||
struct txx9ndfmc_priv *txx9_priv;
|
||||
struct nand_chip *chip;
|
||||
struct mtd_info *mtd;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
int nr_parts;
|
||||
#endif
|
||||
|
||||
if (!(plat->ch_mask & (1 << i)))
|
||||
continue;
|
||||
txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv),
|
||||
GFP_KERNEL);
|
||||
if (!txx9_priv) {
|
||||
dev_err(&dev->dev, "Unable to allocate "
|
||||
"TXx9 NDFMC MTD device structure.\n");
|
||||
continue;
|
||||
}
|
||||
chip = &txx9_priv->chip;
|
||||
mtd = &txx9_priv->mtd;
|
||||
mtd->owner = THIS_MODULE;
|
||||
|
||||
mtd->priv = chip;
|
||||
|
||||
chip->read_byte = txx9ndfmc_read_byte;
|
||||
chip->read_buf = txx9ndfmc_read_buf;
|
||||
chip->write_buf = txx9ndfmc_write_buf;
|
||||
chip->verify_buf = txx9ndfmc_verify_buf;
|
||||
chip->cmd_ctrl = txx9ndfmc_cmd_ctrl;
|
||||
chip->dev_ready = txx9ndfmc_dev_ready;
|
||||
chip->ecc.calculate = txx9ndfmc_calculate_ecc;
|
||||
chip->ecc.correct = nand_correct_data;
|
||||
chip->ecc.hwctl = txx9ndfmc_enable_hwecc;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->chip_delay = 100;
|
||||
chip->controller = &drvdata->hw_control;
|
||||
|
||||
chip->priv = txx9_priv;
|
||||
txx9_priv->dev = dev;
|
||||
|
||||
if (plat->ch_mask != 1) {
|
||||
txx9_priv->cs = i;
|
||||
sprintf(txx9_priv->mtdname, "%s.%u",
|
||||
dev_name(&dev->dev), i);
|
||||
} else {
|
||||
txx9_priv->cs = -1;
|
||||
strcpy(txx9_priv->mtdname, dev_name(&dev->dev));
|
||||
}
|
||||
if (plat->wide_mask & (1 << i))
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
if (nand_scan(mtd, 1)) {
|
||||
kfree(txx9_priv);
|
||||
continue;
|
||||
}
|
||||
mtd->name = txx9_priv->mtdname;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
nr_parts = parse_mtd_partitions(mtd, probes,
|
||||
&drvdata->parts[i], 0);
|
||||
if (nr_parts > 0)
|
||||
add_mtd_partitions(mtd, drvdata->parts[i], nr_parts);
|
||||
#endif
|
||||
add_mtd_device(mtd);
|
||||
drvdata->mtds[i] = mtd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit txx9ndfmc_remove(struct platform_device *dev)
|
||||
{
|
||||
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
if (!drvdata)
|
||||
return 0;
|
||||
for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
|
||||
struct mtd_info *mtd = drvdata->mtds[i];
|
||||
struct nand_chip *chip;
|
||||
struct txx9ndfmc_priv *txx9_priv;
|
||||
|
||||
if (!mtd)
|
||||
continue;
|
||||
chip = mtd->priv;
|
||||
txx9_priv = chip->priv;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
del_mtd_partitions(mtd);
|
||||
kfree(drvdata->parts[i]);
|
||||
#endif
|
||||
del_mtd_device(mtd);
|
||||
kfree(txx9_priv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int txx9ndfmc_resume(struct platform_device *dev)
|
||||
{
|
||||
if (platform_get_drvdata(dev))
|
||||
txx9ndfmc_initialize(dev);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define txx9ndfmc_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver txx9ndfmc_driver = {
|
||||
.remove = __exit_p(txx9ndfmc_remove),
|
||||
.resume = txx9ndfmc_resume,
|
||||
.driver = {
|
||||
.name = "txx9ndfmc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init txx9ndfmc_init(void)
|
||||
{
|
||||
return platform_driver_probe(&txx9ndfmc_driver, txx9ndfmc_probe);
|
||||
}
|
||||
|
||||
static void __exit txx9ndfmc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&txx9ndfmc_driver);
|
||||
}
|
||||
|
||||
module_init(txx9ndfmc_init);
|
||||
module_exit(txx9ndfmc_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver");
|
||||
MODULE_ALIAS("platform:txx9ndfmc");
|
@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ONENAND_SET_NEXT_BUFFERRAM(this);
|
||||
}
|
||||
|
||||
this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
|
||||
|
||||
ret = this->wait(mtd, FL_WRITING);
|
||||
|
||||
/* In partial page write we don't update bufferram */
|
||||
onenand_update_bufferram(mtd, to, !ret && !subpage);
|
||||
/*
|
||||
* 2 PLANE, MLC, and Flex-OneNAND wait here
|
||||
*/
|
||||
if (ONENAND_IS_2PLANE(this)) {
|
||||
ONENAND_SET_BUFFERRAM1(this);
|
||||
onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
|
||||
}
|
||||
ret = this->wait(mtd, FL_WRITING);
|
||||
|
||||
if (ret) {
|
||||
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 (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;
|
||||
}
|
||||
/* 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;
|
||||
written += thislen;
|
||||
|
||||
if (written == len)
|
||||
break;
|
||||
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;
|
||||
|
@ -1,6 +1,6 @@
|
||||
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
|
||||
|
@ -1,7 +1,12 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
|
665
fs/romfs/inode.c
665
fs/romfs/inode.c
@ -1,665 +0,0 @@
|
||||
/*
|
||||
* ROMFS file system, Linux implementation
|
||||
*
|
||||
* Copyright (C) 1997-1999 Janos Farkas <chexum@shadow.banki.hu>
|
||||
*
|
||||
* Using parts of the minix filesystem
|
||||
* Copyright (C) 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* and parts of the affs filesystem additionally
|
||||
* Copyright (C) 1993 Ray Burr
|
||||
* Copyright (C) 1996 Hans-Joachim Widmaier
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Changes
|
||||
* Changed for 2.1.19 modules
|
||||
* Jan 1997 Initial release
|
||||
* Jun 1997 2.1.43+ changes
|
||||
* Proper page locking in readpage
|
||||
* Changed to work with 2.1.45+ fs
|
||||
* Jul 1997 Fixed follow_link
|
||||
* 2.1.47
|
||||
* lookup shouldn't return -ENOENT
|
||||
* from Horst von Brand:
|
||||
* fail on wrong checksum
|
||||
* double unlock_super was possible
|
||||
* correct namelen for statfs
|
||||
* spotted by Bill Hawes:
|
||||
* readlink shouldn't iput()
|
||||
* Jun 1998 2.1.106 from Avery Pennarun: glibc scandir()
|
||||
* exposed a problem in readdir
|
||||
* 2.1.107 code-freeze spellchecker run
|
||||
* Aug 1998 2.1.118+ VFS changes
|
||||
* Sep 1998 2.1.122 another VFS change (follow_link)
|
||||
* Apr 1999 2.2.7 no more EBADF checking in
|
||||
* lookup/readdir, use ERR_PTR
|
||||
* Jun 1999 2.3.6 d_alloc_root use changed
|
||||
* 2.3.9 clean up usage of ENOENT/negative
|
||||
* dentries in lookup
|
||||
* clean up page flags setting
|
||||
* (error, uptodate, locking) in
|
||||
* in readpage
|
||||
* use init_special_inode for
|
||||
* fifos/sockets (and streamline) in
|
||||
* read_inode, fix _ops table order
|
||||
* Aug 1999 2.3.16 __initfunc() => __init change
|
||||
* Oct 1999 2.3.24 page->owner hack obsoleted
|
||||
* Nov 1999 2.3.27 2.3.25+ page->offset => index change
|
||||
*/
|
||||
|
||||
/* todo:
|
||||
* - see Documentation/filesystems/romfs.txt
|
||||
* - use allocated, not stack memory for file names?
|
||||
* - considering write access...
|
||||
* - network (tftp) files?
|
||||
* - merge back some _op tables
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sorry about some optimizations and for some goto's. I just wanted
|
||||
* to squeeze some more bytes out of this code.. :)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/romfs_fs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/vfs.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
struct romfs_inode_info {
|
||||
unsigned long i_metasize; /* size of non-data area */
|
||||
unsigned long i_dataoffset; /* from the start of fs */
|
||||
struct inode vfs_inode;
|
||||
};
|
||||
|
||||
static struct inode *romfs_iget(struct super_block *, unsigned long);
|
||||
|
||||
/* instead of private superblock data */
|
||||
static inline unsigned long romfs_maxsize(struct super_block *sb)
|
||||
{
|
||||
return (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);
|
||||
}
|
||||
|
||||
static __u32
|
||||
romfs_checksum(void *data, int size)
|
||||
{
|
||||
__u32 sum;
|
||||
__be32 *ptr;
|
||||
|
||||
sum = 0; ptr = data;
|
||||
size>>=2;
|
||||
while (size>0) {
|
||||
sum += be32_to_cpu(*ptr++);
|
||||
size--;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static const struct super_operations romfs_ops;
|
||||
|
||||
static int romfs_fill_super(struct super_block *s, void *data, int silent)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
struct romfs_super_block *rsb;
|
||||
struct inode *root;
|
||||
int sz, ret = -EINVAL;
|
||||
|
||||
/* I would parse the options here, but there are none.. :) */
|
||||
|
||||
sb_set_blocksize(s, ROMBSIZE);
|
||||
s->s_maxbytes = 0xFFFFFFFF;
|
||||
|
||||
bh = sb_bread(s, 0);
|
||||
if (!bh) {
|
||||
/* XXX merge with other printk? */
|
||||
printk ("romfs: unable to read superblock\n");
|
||||
goto outnobh;
|
||||
}
|
||||
|
||||
rsb = (struct romfs_super_block *)bh->b_data;
|
||||
sz = be32_to_cpu(rsb->size);
|
||||
if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
|
||||
|| sz < ROMFH_SIZE) {
|
||||
if (!silent)
|
||||
printk ("VFS: Can't find a romfs filesystem on dev "
|
||||
"%s.\n", s->s_id);
|
||||
goto out;
|
||||
}
|
||||
if (romfs_checksum(rsb, min_t(int, sz, 512))) {
|
||||
printk ("romfs: bad initial checksum on dev "
|
||||
"%s.\n", s->s_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
s->s_magic = ROMFS_MAGIC;
|
||||
s->s_fs_info = (void *)(long)sz;
|
||||
|
||||
s->s_flags |= MS_RDONLY;
|
||||
|
||||
/* Find the start of the fs */
|
||||
sz = (ROMFH_SIZE +
|
||||
strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
|
||||
& ROMFH_MASK;
|
||||
|
||||
s->s_op = &romfs_ops;
|
||||
root = romfs_iget(s, sz);
|
||||
if (IS_ERR(root)) {
|
||||
ret = PTR_ERR(root);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -ENOMEM;
|
||||
s->s_root = d_alloc_root(root);
|
||||
if (!s->s_root)
|
||||
goto outiput;
|
||||
|
||||
brelse(bh);
|
||||
return 0;
|
||||
|
||||
outiput:
|
||||
iput(root);
|
||||
out:
|
||||
brelse(bh);
|
||||
outnobh:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* That's simple too. */
|
||||
|
||||
static int
|
||||
romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
buf->f_type = ROMFS_MAGIC;
|
||||
buf->f_bsize = ROMBSIZE;
|
||||
buf->f_bfree = buf->f_bavail = buf->f_ffree;
|
||||
buf->f_blocks = (romfs_maxsize(dentry->d_sb)+ROMBSIZE-1)>>ROMBSBITS;
|
||||
buf->f_namelen = ROMFS_MAXFN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* some helper routines */
|
||||
|
||||
static int
|
||||
romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
unsigned long avail, maxsize, res;
|
||||
|
||||
maxsize = romfs_maxsize(i->i_sb);
|
||||
if (offset >= maxsize)
|
||||
return -1;
|
||||
|
||||
/* strnlen is almost always valid */
|
||||
if (count > maxsize || offset+count > maxsize)
|
||||
count = maxsize-offset;
|
||||
|
||||
bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
|
||||
if (!bh)
|
||||
return -1; /* error */
|
||||
|
||||
avail = ROMBSIZE - (offset & ROMBMASK);
|
||||
maxsize = min_t(unsigned long, count, avail);
|
||||
res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
|
||||
brelse(bh);
|
||||
|
||||
if (res < maxsize)
|
||||
return res; /* found all of it */
|
||||
|
||||
while (res < count) {
|
||||
offset += maxsize;
|
||||
|
||||
bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
|
||||
if (!bh)
|
||||
return -1;
|
||||
maxsize = min_t(unsigned long, count - res, ROMBSIZE);
|
||||
avail = strnlen(bh->b_data, maxsize);
|
||||
res += avail;
|
||||
brelse(bh);
|
||||
if (avail < maxsize)
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
|
||||
{
|
||||
struct buffer_head *bh;
|
||||
unsigned long avail, maxsize, res;
|
||||
|
||||
maxsize = romfs_maxsize(i->i_sb);
|
||||
if (offset >= maxsize || count > maxsize || offset+count>maxsize)
|
||||
return -1;
|
||||
|
||||
bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
|
||||
if (!bh)
|
||||
return -1; /* error */
|
||||
|
||||
avail = ROMBSIZE - (offset & ROMBMASK);
|
||||
maxsize = min_t(unsigned long, count, avail);
|
||||
memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
|
||||
brelse(bh);
|
||||
|
||||
res = maxsize; /* all of it */
|
||||
|
||||
while (res < count) {
|
||||
offset += maxsize;
|
||||
dest += maxsize;
|
||||
|
||||
bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
|
||||
if (!bh)
|
||||
return -1;
|
||||
maxsize = min_t(unsigned long, count - res, ROMBSIZE);
|
||||
memcpy(dest, bh->b_data, maxsize);
|
||||
brelse(bh);
|
||||
res += maxsize;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned char romfs_dtype_table[] = {
|
||||
DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
|
||||
};
|
||||
|
||||
static int
|
||||
romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
struct inode *i = filp->f_path.dentry->d_inode;
|
||||
struct romfs_inode ri;
|
||||
unsigned long offset, maxoff;
|
||||
int j, ino, nextfh;
|
||||
int stored = 0;
|
||||
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
||||
|
||||
lock_kernel();
|
||||
|
||||
maxoff = romfs_maxsize(i->i_sb);
|
||||
|
||||
offset = filp->f_pos;
|
||||
if (!offset) {
|
||||
offset = i->i_ino & ROMFH_MASK;
|
||||
if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
|
||||
goto out;
|
||||
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
}
|
||||
|
||||
/* Not really failsafe, but we are read-only... */
|
||||
for(;;) {
|
||||
if (!offset || offset >= maxoff) {
|
||||
offset = maxoff;
|
||||
filp->f_pos = offset;
|
||||
goto out;
|
||||
}
|
||||
filp->f_pos = offset;
|
||||
|
||||
/* Fetch inode info */
|
||||
if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
|
||||
goto out;
|
||||
|
||||
j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
|
||||
if (j < 0)
|
||||
goto out;
|
||||
|
||||
fsname[j]=0;
|
||||
romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
|
||||
|
||||
ino = offset;
|
||||
nextfh = be32_to_cpu(ri.next);
|
||||
if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
|
||||
ino = be32_to_cpu(ri.spec);
|
||||
if (filldir(dirent, fsname, j, offset, ino,
|
||||
romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) {
|
||||
goto out;
|
||||
}
|
||||
stored++;
|
||||
offset = nextfh & ROMFH_MASK;
|
||||
}
|
||||
out:
|
||||
unlock_kernel();
|
||||
return stored;
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
romfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
unsigned long offset, maxoff;
|
||||
long res;
|
||||
int fslen;
|
||||
struct inode *inode = NULL;
|
||||
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
||||
struct romfs_inode ri;
|
||||
const char *name; /* got from dentry */
|
||||
int len;
|
||||
|
||||
res = -EACCES; /* placeholder for "no data here" */
|
||||
offset = dir->i_ino & ROMFH_MASK;
|
||||
lock_kernel();
|
||||
if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
|
||||
goto error;
|
||||
|
||||
maxoff = romfs_maxsize(dir->i_sb);
|
||||
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
|
||||
/* OK, now find the file whose name is in "dentry" in the
|
||||
* directory specified by "dir". */
|
||||
|
||||
name = dentry->d_name.name;
|
||||
len = dentry->d_name.len;
|
||||
|
||||
for(;;) {
|
||||
if (!offset || offset >= maxoff)
|
||||
goto success; /* negative success */
|
||||
if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
|
||||
goto error;
|
||||
|
||||
/* try to match the first 16 bytes of name */
|
||||
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
|
||||
if (len < ROMFH_SIZE) {
|
||||
if (len == fslen) {
|
||||
/* both are shorter, and same size */
|
||||
romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
|
||||
if (strncmp (name, fsname, len) == 0)
|
||||
break;
|
||||
}
|
||||
} else if (fslen >= ROMFH_SIZE) {
|
||||
/* both are longer; XXX optimize max size */
|
||||
fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
|
||||
if (len == fslen) {
|
||||
romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
|
||||
if (strncmp(name, fsname, len) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* next entry */
|
||||
offset = be32_to_cpu(ri.next) & ROMFH_MASK;
|
||||
}
|
||||
|
||||
/* Hard link handling */
|
||||
if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
|
||||
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
|
||||
inode = romfs_iget(dir->i_sb, offset);
|
||||
if (IS_ERR(inode)) {
|
||||
res = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
|
||||
success:
|
||||
d_add(dentry, inode);
|
||||
res = 0;
|
||||
error:
|
||||
unlock_kernel();
|
||||
return ERR_PTR(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we do readpage, to be able to execute programs. Unfortunately,
|
||||
* we can't use bmap, since we may have looser alignments.
|
||||
*/
|
||||
|
||||
static int
|
||||
romfs_readpage(struct file *file, struct page * page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
loff_t offset, size;
|
||||
unsigned long filled;
|
||||
void *buf;
|
||||
int result = -EIO;
|
||||
|
||||
page_cache_get(page);
|
||||
lock_kernel();
|
||||
buf = kmap(page);
|
||||
if (!buf)
|
||||
goto err_out;
|
||||
|
||||
/* 32 bit warning -- but not for us :) */
|
||||
offset = page_offset(page);
|
||||
size = i_size_read(inode);
|
||||
filled = 0;
|
||||
result = 0;
|
||||
if (offset < size) {
|
||||
unsigned long readlen;
|
||||
|
||||
size -= offset;
|
||||
readlen = size > PAGE_SIZE ? PAGE_SIZE : size;
|
||||
|
||||
filled = romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen);
|
||||
|
||||
if (filled != readlen) {
|
||||
SetPageError(page);
|
||||
filled = 0;
|
||||
result = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (filled < PAGE_SIZE)
|
||||
memset(buf + filled, 0, PAGE_SIZE-filled);
|
||||
|
||||
if (!result)
|
||||
SetPageUptodate(page);
|
||||
flush_dcache_page(page);
|
||||
|
||||
unlock_page(page);
|
||||
|
||||
kunmap(page);
|
||||
err_out:
|
||||
page_cache_release(page);
|
||||
unlock_kernel();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Mapping from our types to the kernel */
|
||||
|
||||
static const struct address_space_operations romfs_aops = {
|
||||
.readpage = romfs_readpage
|
||||
};
|
||||
|
||||
static const struct file_operations romfs_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.readdir = romfs_readdir,
|
||||
};
|
||||
|
||||
static const struct inode_operations romfs_dir_inode_operations = {
|
||||
.lookup = romfs_lookup,
|
||||
};
|
||||
|
||||
static mode_t romfs_modemap[] =
|
||||
{
|
||||
0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777,
|
||||
S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644
|
||||
};
|
||||
|
||||
static struct inode *
|
||||
romfs_iget(struct super_block *sb, unsigned long ino)
|
||||
{
|
||||
int nextfh, ret;
|
||||
struct romfs_inode ri;
|
||||
struct inode *i;
|
||||
|
||||
ino &= ROMFH_MASK;
|
||||
i = iget_locked(sb, ino);
|
||||
if (!i)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!(i->i_state & I_NEW))
|
||||
return i;
|
||||
|
||||
i->i_mode = 0;
|
||||
|
||||
/* Loop for finding the real hard link */
|
||||
for(;;) {
|
||||
if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
|
||||
printk(KERN_ERR "romfs: read error for inode 0x%lx\n",
|
||||
ino);
|
||||
iget_failed(i);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
/* XXX: do romfs_checksum here too (with name) */
|
||||
|
||||
nextfh = be32_to_cpu(ri.next);
|
||||
if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
|
||||
break;
|
||||
|
||||
ino = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
}
|
||||
|
||||
i->i_nlink = 1; /* Hard to decide.. */
|
||||
i->i_size = be32_to_cpu(ri.size);
|
||||
i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0;
|
||||
i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0;
|
||||
|
||||
/* Precalculate the data offset */
|
||||
ret = romfs_strnlen(i, ino + ROMFH_SIZE, ROMFS_MAXFN);
|
||||
if (ret >= 0)
|
||||
ino = (ROMFH_SIZE + ret + 1 + ROMFH_PAD) & ROMFH_MASK;
|
||||
else
|
||||
ino = 0;
|
||||
|
||||
ROMFS_I(i)->i_metasize = ino;
|
||||
ROMFS_I(i)->i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
|
||||
|
||||
/* Compute permissions */
|
||||
ino = romfs_modemap[nextfh & ROMFH_TYPE];
|
||||
/* only "normal" files have ops */
|
||||
switch (nextfh & ROMFH_TYPE) {
|
||||
case 1:
|
||||
i->i_size = ROMFS_I(i)->i_metasize;
|
||||
i->i_op = &romfs_dir_inode_operations;
|
||||
i->i_fop = &romfs_dir_operations;
|
||||
if (nextfh & ROMFH_EXEC)
|
||||
ino |= S_IXUGO;
|
||||
i->i_mode = ino;
|
||||
break;
|
||||
case 2:
|
||||
i->i_fop = &generic_ro_fops;
|
||||
i->i_data.a_ops = &romfs_aops;
|
||||
if (nextfh & ROMFH_EXEC)
|
||||
ino |= S_IXUGO;
|
||||
i->i_mode = ino;
|
||||
break;
|
||||
case 3:
|
||||
i->i_op = &page_symlink_inode_operations;
|
||||
i->i_data.a_ops = &romfs_aops;
|
||||
i->i_mode = ino | S_IRWXUGO;
|
||||
break;
|
||||
default:
|
||||
/* depending on MBZ for sock/fifos */
|
||||
nextfh = be32_to_cpu(ri.spec);
|
||||
init_special_inode(i, ino,
|
||||
MKDEV(nextfh>>16,nextfh&0xffff));
|
||||
}
|
||||
unlock_new_inode(i);
|
||||
return i;
|
||||
}
|
||||
|
||||
static struct kmem_cache * romfs_inode_cachep;
|
||||
|
||||
static struct inode *romfs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct romfs_inode_info *ei;
|
||||
ei = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
return &ei->vfs_inode;
|
||||
}
|
||||
|
||||
static void romfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode));
|
||||
}
|
||||
|
||||
static void init_once(void *foo)
|
||||
{
|
||||
struct romfs_inode_info *ei = foo;
|
||||
|
||||
inode_init_once(&ei->vfs_inode);
|
||||
}
|
||||
|
||||
static int init_inodecache(void)
|
||||
{
|
||||
romfs_inode_cachep = kmem_cache_create("romfs_inode_cache",
|
||||
sizeof(struct romfs_inode_info),
|
||||
0, (SLAB_RECLAIM_ACCOUNT|
|
||||
SLAB_MEM_SPREAD),
|
||||
init_once);
|
||||
if (romfs_inode_cachep == NULL)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_inodecache(void)
|
||||
{
|
||||
kmem_cache_destroy(romfs_inode_cachep);
|
||||
}
|
||||
|
||||
static int romfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
*flags |= MS_RDONLY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations romfs_ops = {
|
||||
.alloc_inode = romfs_alloc_inode,
|
||||
.destroy_inode = romfs_destroy_inode,
|
||||
.statfs = romfs_statfs,
|
||||
.remount_fs = romfs_remount,
|
||||
};
|
||||
|
||||
static int romfs_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name, void *data, struct vfsmount *mnt)
|
||||
{
|
||||
return get_sb_bdev(fs_type, flags, dev_name, data, romfs_fill_super,
|
||||
mnt);
|
||||
}
|
||||
|
||||
static struct file_system_type romfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "romfs",
|
||||
.get_sb = romfs_get_sb,
|
||||
.kill_sb = kill_block_super,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
|
||||
static int __init init_romfs_fs(void)
|
||||
{
|
||||
int err = init_inodecache();
|
||||
if (err)
|
||||
goto out1;
|
||||
err = register_filesystem(&romfs_fs_type);
|
||||
if (err)
|
||||
goto out;
|
||||
return 0;
|
||||
out:
|
||||
destroy_inodecache();
|
||||
out1:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit exit_romfs_fs(void)
|
||||
{
|
||||
unregister_filesystem(&romfs_fs_type);
|
||||
destroy_inodecache();
|
||||
}
|
||||
|
||||
/* Yes, works even as a module... :) */
|
||||
|
||||
module_init(init_romfs_fs)
|
||||
module_exit(exit_romfs_fs)
|
||||
MODULE_LICENSE("GPL");
|
47
fs/romfs/internal.h
Normal file
47
fs/romfs/internal.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* 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);
|
75
fs/romfs/mmap-nommu.c
Normal file
75
fs/romfs/mmap-nommu.c
Normal file
@ -0,0 +1,75 @@
|
||||
/* 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,
|
||||
};
|
261
fs/romfs/storage.c
Normal file
261
fs/romfs/storage.c
Normal file
@ -0,0 +1,261 @@
|
||||
/* 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;
|
||||
}
|
648
fs/romfs/super.c
Normal file
648
fs/romfs/super.c
Normal file
@ -0,0 +1,648 @@
|
||||
/* Block- or MTD-based romfs
|
||||
*
|
||||
* Copyright © 2007 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* Derived from: ROMFS file system, Linux implementation
|
||||
*
|
||||
* Copyright © 1997-1999 Janos Farkas <chexum@shadow.banki.hu>
|
||||
*
|
||||
* Using parts of the minix filesystem
|
||||
* Copyright © 1991, 1992 Linus Torvalds
|
||||
*
|
||||
* and parts of the affs filesystem additionally
|
||||
* Copyright © 1993 Ray Burr
|
||||
* Copyright © 1996 Hans-Joachim Widmaier
|
||||
*
|
||||
* Changes
|
||||
* Changed for 2.1.19 modules
|
||||
* Jan 1997 Initial release
|
||||
* Jun 1997 2.1.43+ changes
|
||||
* Proper page locking in readpage
|
||||
* Changed to work with 2.1.45+ fs
|
||||
* Jul 1997 Fixed follow_link
|
||||
* 2.1.47
|
||||
* lookup shouldn't return -ENOENT
|
||||
* from Horst von Brand:
|
||||
* fail on wrong checksum
|
||||
* double unlock_super was possible
|
||||
* correct namelen for statfs
|
||||
* spotted by Bill Hawes:
|
||||
* readlink shouldn't iput()
|
||||
* Jun 1998 2.1.106 from Avery Pennarun: glibc scandir()
|
||||
* exposed a problem in readdir
|
||||
* 2.1.107 code-freeze spellchecker run
|
||||
* Aug 1998 2.1.118+ VFS changes
|
||||
* Sep 1998 2.1.122 another VFS change (follow_link)
|
||||
* Apr 1999 2.2.7 no more EBADF checking in
|
||||
* lookup/readdir, use ERR_PTR
|
||||
* Jun 1999 2.3.6 d_alloc_root use changed
|
||||
* 2.3.9 clean up usage of ENOENT/negative
|
||||
* dentries in lookup
|
||||
* clean up page flags setting
|
||||
* (error, uptodate, locking) in
|
||||
* in readpage
|
||||
* use init_special_inode for
|
||||
* fifos/sockets (and streamline) in
|
||||
* read_inode, fix _ops table order
|
||||
* Aug 1999 2.3.16 __initfunc() => __init change
|
||||
* Oct 1999 2.3.24 page->owner hack obsoleted
|
||||
* Nov 1999 2.3.27 2.3.25+ page->offset => index change
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/statfs.h>
|
||||
#include <linux/mtd/super.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "internal.h"
|
||||
|
||||
static struct kmem_cache *romfs_inode_cachep;
|
||||
|
||||
static const umode_t romfs_modemap[8] = {
|
||||
0, /* hard link */
|
||||
S_IFDIR | 0644, /* directory */
|
||||
S_IFREG | 0644, /* regular file */
|
||||
S_IFLNK | 0777, /* symlink */
|
||||
S_IFBLK | 0600, /* blockdev */
|
||||
S_IFCHR | 0600, /* chardev */
|
||||
S_IFSOCK | 0644, /* socket */
|
||||
S_IFIFO | 0644 /* FIFO */
|
||||
};
|
||||
|
||||
static const unsigned char romfs_dtype_table[] = {
|
||||
DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
|
||||
};
|
||||
|
||||
static struct inode *romfs_iget(struct super_block *sb, unsigned long pos);
|
||||
|
||||
/*
|
||||
* read a page worth of data from the image
|
||||
*/
|
||||
static int romfs_readpage(struct file *file, struct page *page)
|
||||
{
|
||||
struct inode *inode = page->mapping->host;
|
||||
loff_t offset, size;
|
||||
unsigned long fillsize, pos;
|
||||
void *buf;
|
||||
int ret;
|
||||
|
||||
buf = kmap(page);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* 32 bit warning -- but not for us :) */
|
||||
offset = page_offset(page);
|
||||
size = i_size_read(inode);
|
||||
fillsize = 0;
|
||||
ret = 0;
|
||||
if (offset < size) {
|
||||
size -= offset;
|
||||
fillsize = size > PAGE_SIZE ? PAGE_SIZE : size;
|
||||
|
||||
pos = ROMFS_I(inode)->i_dataoffset + offset;
|
||||
|
||||
ret = romfs_dev_read(inode->i_sb, pos, buf, fillsize);
|
||||
if (ret < 0) {
|
||||
SetPageError(page);
|
||||
fillsize = 0;
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (fillsize < PAGE_SIZE)
|
||||
memset(buf + fillsize, 0, PAGE_SIZE - fillsize);
|
||||
if (ret == 0)
|
||||
SetPageUptodate(page);
|
||||
|
||||
flush_dcache_page(page);
|
||||
kunmap(page);
|
||||
unlock_page(page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct address_space_operations romfs_aops = {
|
||||
.readpage = romfs_readpage
|
||||
};
|
||||
|
||||
/*
|
||||
* read the entries from a directory
|
||||
*/
|
||||
static int romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
struct inode *i = filp->f_dentry->d_inode;
|
||||
struct romfs_inode ri;
|
||||
unsigned long offset, maxoff;
|
||||
int j, ino, nextfh;
|
||||
int stored = 0;
|
||||
char fsname[ROMFS_MAXFN]; /* XXX dynamic? */
|
||||
int ret;
|
||||
|
||||
maxoff = romfs_maxsize(i->i_sb);
|
||||
|
||||
offset = filp->f_pos;
|
||||
if (!offset) {
|
||||
offset = i->i_ino & ROMFH_MASK;
|
||||
ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
}
|
||||
|
||||
/* Not really failsafe, but we are read-only... */
|
||||
for (;;) {
|
||||
if (!offset || offset >= maxoff) {
|
||||
offset = maxoff;
|
||||
filp->f_pos = offset;
|
||||
goto out;
|
||||
}
|
||||
filp->f_pos = offset;
|
||||
|
||||
/* Fetch inode info */
|
||||
ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE,
|
||||
sizeof(fsname) - 1);
|
||||
if (j < 0)
|
||||
goto out;
|
||||
|
||||
ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
fsname[j] = '\0';
|
||||
|
||||
ino = offset;
|
||||
nextfh = be32_to_cpu(ri.next);
|
||||
if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
|
||||
ino = be32_to_cpu(ri.spec);
|
||||
if (filldir(dirent, fsname, j, offset, ino,
|
||||
romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0)
|
||||
goto out;
|
||||
|
||||
stored++;
|
||||
offset = nextfh & ROMFH_MASK;
|
||||
}
|
||||
|
||||
out:
|
||||
return stored;
|
||||
}
|
||||
|
||||
/*
|
||||
* look up an entry in a directory
|
||||
*/
|
||||
static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
struct nameidata *nd)
|
||||
{
|
||||
unsigned long offset, maxoff;
|
||||
struct inode *inode;
|
||||
struct romfs_inode ri;
|
||||
const char *name; /* got from dentry */
|
||||
int len, ret;
|
||||
|
||||
offset = dir->i_ino & ROMFH_MASK;
|
||||
ret = romfs_dev_read(dir->i_sb, offset, &ri, ROMFH_SIZE);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* search all the file entries in the list starting from the one
|
||||
* pointed to by the directory's special data */
|
||||
maxoff = romfs_maxsize(dir->i_sb);
|
||||
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
|
||||
name = dentry->d_name.name;
|
||||
len = dentry->d_name.len;
|
||||
|
||||
for (;;) {
|
||||
if (!offset || offset >= maxoff)
|
||||
goto out0;
|
||||
|
||||
ret = romfs_dev_read(dir->i_sb, offset, &ri, sizeof(ri));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* try to match the first 16 bytes of name */
|
||||
ret = romfs_dev_strncmp(dir->i_sb, offset + ROMFH_SIZE, name,
|
||||
len);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
if (ret == 1)
|
||||
break;
|
||||
|
||||
/* next entry */
|
||||
offset = be32_to_cpu(ri.next) & ROMFH_MASK;
|
||||
}
|
||||
|
||||
/* Hard link handling */
|
||||
if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
|
||||
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
|
||||
inode = romfs_iget(dir->i_sb, offset);
|
||||
if (IS_ERR(inode)) {
|
||||
ret = PTR_ERR(inode);
|
||||
goto error;
|
||||
}
|
||||
goto outi;
|
||||
|
||||
/*
|
||||
* it's a bit funky, _lookup needs to return an error code
|
||||
* (negative) or a NULL, both as a dentry. ENOENT should not
|
||||
* be returned, instead we need to create a negative dentry by
|
||||
* d_add(dentry, NULL); and return 0 as no error.
|
||||
* (Although as I see, it only matters on writable file
|
||||
* systems).
|
||||
*/
|
||||
out0:
|
||||
inode = NULL;
|
||||
outi:
|
||||
d_add(dentry, inode);
|
||||
ret = 0;
|
||||
error:
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static const struct file_operations romfs_dir_operations = {
|
||||
.read = generic_read_dir,
|
||||
.readdir = romfs_readdir,
|
||||
};
|
||||
|
||||
static struct inode_operations romfs_dir_inode_operations = {
|
||||
.lookup = romfs_lookup,
|
||||
};
|
||||
|
||||
/*
|
||||
* get a romfs inode based on its position in the image (which doubles as the
|
||||
* inode number)
|
||||
*/
|
||||
static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
|
||||
{
|
||||
struct romfs_inode_info *inode;
|
||||
struct romfs_inode ri;
|
||||
struct inode *i;
|
||||
unsigned long nlen;
|
||||
unsigned nextfh, ret;
|
||||
umode_t mode;
|
||||
|
||||
/* we might have to traverse a chain of "hard link" file entries to get
|
||||
* to the actual file */
|
||||
for (;;) {
|
||||
ret = romfs_dev_read(sb, pos, &ri, sizeof(ri));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/* XXX: do romfs_checksum here too (with name) */
|
||||
|
||||
nextfh = be32_to_cpu(ri.next);
|
||||
if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
|
||||
break;
|
||||
|
||||
pos = be32_to_cpu(ri.spec) & ROMFH_MASK;
|
||||
}
|
||||
|
||||
/* determine the length of the filename */
|
||||
nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN);
|
||||
if (IS_ERR_VALUE(nlen))
|
||||
goto eio;
|
||||
|
||||
/* get an inode for this image position */
|
||||
i = iget_locked(sb, pos);
|
||||
if (!i)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!(i->i_state & I_NEW))
|
||||
return i;
|
||||
|
||||
/* precalculate the data offset */
|
||||
inode = ROMFS_I(i);
|
||||
inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK;
|
||||
inode->i_dataoffset = pos + inode->i_metasize;
|
||||
|
||||
i->i_nlink = 1; /* Hard to decide.. */
|
||||
i->i_size = be32_to_cpu(ri.size);
|
||||
i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0;
|
||||
i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0;
|
||||
|
||||
/* set up mode and ops */
|
||||
mode = romfs_modemap[nextfh & ROMFH_TYPE];
|
||||
|
||||
switch (nextfh & ROMFH_TYPE) {
|
||||
case ROMFH_DIR:
|
||||
i->i_size = ROMFS_I(i)->i_metasize;
|
||||
i->i_op = &romfs_dir_inode_operations;
|
||||
i->i_fop = &romfs_dir_operations;
|
||||
if (nextfh & ROMFH_EXEC)
|
||||
mode |= S_IXUGO;
|
||||
break;
|
||||
case ROMFH_REG:
|
||||
i->i_fop = &romfs_ro_fops;
|
||||
i->i_data.a_ops = &romfs_aops;
|
||||
if (i->i_sb->s_mtd)
|
||||
i->i_data.backing_dev_info =
|
||||
i->i_sb->s_mtd->backing_dev_info;
|
||||
if (nextfh & ROMFH_EXEC)
|
||||
mode |= S_IXUGO;
|
||||
break;
|
||||
case ROMFH_SYM:
|
||||
i->i_op = &page_symlink_inode_operations;
|
||||
i->i_data.a_ops = &romfs_aops;
|
||||
mode |= S_IRWXUGO;
|
||||
break;
|
||||
default:
|
||||
/* depending on MBZ for sock/fifos */
|
||||
nextfh = be32_to_cpu(ri.spec);
|
||||
init_special_inode(i, mode, MKDEV(nextfh >> 16,
|
||||
nextfh & 0xffff));
|
||||
break;
|
||||
}
|
||||
|
||||
i->i_mode = mode;
|
||||
|
||||
unlock_new_inode(i);
|
||||
return i;
|
||||
|
||||
eio:
|
||||
ret = -EIO;
|
||||
error:
|
||||
printk(KERN_ERR "ROMFS: read error for inode 0x%lx\n", pos);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a new inode
|
||||
*/
|
||||
static struct inode *romfs_alloc_inode(struct super_block *sb)
|
||||
{
|
||||
struct romfs_inode_info *inode;
|
||||
inode = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL);
|
||||
return inode ? &inode->vfs_inode : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* return a spent inode to the slab cache
|
||||
*/
|
||||
static void romfs_destroy_inode(struct inode *inode)
|
||||
{
|
||||
kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode));
|
||||
}
|
||||
|
||||
/*
|
||||
* get filesystem statistics
|
||||
*/
|
||||
static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
{
|
||||
buf->f_type = ROMFS_MAGIC;
|
||||
buf->f_namelen = ROMFS_MAXFN;
|
||||
buf->f_bsize = ROMBSIZE;
|
||||
buf->f_bfree = buf->f_bavail = buf->f_ffree;
|
||||
buf->f_blocks =
|
||||
(romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* remounting must involve read-only
|
||||
*/
|
||||
static int romfs_remount(struct super_block *sb, int *flags, char *data)
|
||||
{
|
||||
*flags |= MS_RDONLY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct super_operations romfs_super_ops = {
|
||||
.alloc_inode = romfs_alloc_inode,
|
||||
.destroy_inode = romfs_destroy_inode,
|
||||
.statfs = romfs_statfs,
|
||||
.remount_fs = romfs_remount,
|
||||
};
|
||||
|
||||
/*
|
||||
* checksum check on part of a romfs filesystem
|
||||
*/
|
||||
static __u32 romfs_checksum(const void *data, int size)
|
||||
{
|
||||
const __be32 *ptr = data;
|
||||
__u32 sum;
|
||||
|
||||
sum = 0;
|
||||
size >>= 2;
|
||||
while (size > 0) {
|
||||
sum += be32_to_cpu(*ptr++);
|
||||
size--;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* fill in the superblock
|
||||
*/
|
||||
static int romfs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
{
|
||||
struct romfs_super_block *rsb;
|
||||
struct inode *root;
|
||||
unsigned long pos, img_size;
|
||||
const char *storage;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_BLOCK
|
||||
if (!sb->s_mtd) {
|
||||
sb_set_blocksize(sb, ROMBSIZE);
|
||||
} else {
|
||||
sb->s_blocksize = ROMBSIZE;
|
||||
sb->s_blocksize_bits = blksize_bits(ROMBSIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
sb->s_maxbytes = 0xFFFFFFFF;
|
||||
sb->s_magic = ROMFS_MAGIC;
|
||||
sb->s_flags |= MS_RDONLY | MS_NOATIME;
|
||||
sb->s_op = &romfs_super_ops;
|
||||
|
||||
/* read the image superblock and check it */
|
||||
rsb = kmalloc(512, GFP_KERNEL);
|
||||
if (!rsb)
|
||||
return -ENOMEM;
|
||||
|
||||
sb->s_fs_info = (void *) 512;
|
||||
ret = romfs_dev_read(sb, 0, rsb, 512);
|
||||
if (ret < 0)
|
||||
goto error_rsb;
|
||||
|
||||
img_size = be32_to_cpu(rsb->size);
|
||||
|
||||
if (sb->s_mtd && img_size > sb->s_mtd->size)
|
||||
goto error_rsb_inval;
|
||||
|
||||
sb->s_fs_info = (void *) img_size;
|
||||
|
||||
if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 ||
|
||||
img_size < ROMFH_SIZE) {
|
||||
if (!silent)
|
||||
printk(KERN_WARNING "VFS:"
|
||||
" Can't find a romfs filesystem on dev %s.\n",
|
||||
sb->s_id);
|
||||
goto error_rsb_inval;
|
||||
}
|
||||
|
||||
if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) {
|
||||
printk(KERN_ERR "ROMFS: bad initial checksum on dev %s.\n",
|
||||
sb->s_id);
|
||||
goto error_rsb_inval;
|
||||
}
|
||||
|
||||
storage = sb->s_mtd ? "MTD" : "the block layer";
|
||||
|
||||
len = strnlen(rsb->name, ROMFS_MAXFN);
|
||||
if (!silent)
|
||||
printk(KERN_NOTICE "ROMFS: Mounting image '%*.*s' through %s\n",
|
||||
(unsigned) len, (unsigned) len, rsb->name, storage);
|
||||
|
||||
kfree(rsb);
|
||||
rsb = NULL;
|
||||
|
||||
/* find the root directory */
|
||||
pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK;
|
||||
|
||||
root = romfs_iget(sb, pos);
|
||||
if (!root)
|
||||
goto error;
|
||||
|
||||
sb->s_root = d_alloc_root(root);
|
||||
if (!sb->s_root)
|
||||
goto error_i;
|
||||
|
||||
return 0;
|
||||
|
||||
error_i:
|
||||
iput(root);
|
||||
error:
|
||||
return -EINVAL;
|
||||
error_rsb_inval:
|
||||
ret = -EINVAL;
|
||||
error_rsb:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a superblock for mounting
|
||||
*/
|
||||
static int romfs_get_sb(struct file_system_type *fs_type,
|
||||
int flags, const char *dev_name,
|
||||
void *data, struct vfsmount *mnt)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
#ifdef CONFIG_ROMFS_ON_MTD
|
||||
ret = get_sb_mtd(fs_type, flags, dev_name, data, romfs_fill_super,
|
||||
mnt);
|
||||
#endif
|
||||
#ifdef CONFIG_ROMFS_ON_BLOCK
|
||||
if (ret == -EINVAL)
|
||||
ret = get_sb_bdev(fs_type, flags, dev_name, data,
|
||||
romfs_fill_super, mnt);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* destroy a romfs superblock in the appropriate manner
|
||||
*/
|
||||
static void romfs_kill_sb(struct super_block *sb)
|
||||
{
|
||||
#ifdef CONFIG_ROMFS_ON_MTD
|
||||
if (sb->s_mtd) {
|
||||
kill_mtd_super(sb);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_ROMFS_ON_BLOCK
|
||||
if (sb->s_bdev) {
|
||||
kill_block_super(sb);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct file_system_type romfs_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "romfs",
|
||||
.get_sb = romfs_get_sb,
|
||||
.kill_sb = romfs_kill_sb,
|
||||
.fs_flags = FS_REQUIRES_DEV,
|
||||
};
|
||||
|
||||
/*
|
||||
* inode storage initialiser
|
||||
*/
|
||||
static void romfs_i_init_once(void *_inode)
|
||||
{
|
||||
struct romfs_inode_info *inode = _inode;
|
||||
|
||||
inode_init_once(&inode->vfs_inode);
|
||||
}
|
||||
|
||||
/*
|
||||
* romfs module initialisation
|
||||
*/
|
||||
static int __init init_romfs_fs(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
printk(KERN_INFO "ROMFS MTD (C) 2007 Red Hat, Inc.\n");
|
||||
|
||||
romfs_inode_cachep =
|
||||
kmem_cache_create("romfs_i",
|
||||
sizeof(struct romfs_inode_info), 0,
|
||||
SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
|
||||
romfs_i_init_once);
|
||||
|
||||
if (!romfs_inode_cachep) {
|
||||
printk(KERN_ERR
|
||||
"ROMFS error: Failed to initialise inode cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = register_filesystem(&romfs_fs_type);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "ROMFS error: Failed to register filesystem\n");
|
||||
goto error_register;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_register:
|
||||
kmem_cache_destroy(romfs_inode_cachep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* romfs module removal
|
||||
*/
|
||||
static void __exit exit_romfs_fs(void)
|
||||
{
|
||||
unregister_filesystem(&romfs_fs_type);
|
||||
kmem_cache_destroy(romfs_inode_cachep);
|
||||
}
|
||||
|
||||
module_init(init_romfs_fs);
|
||||
module_exit(exit_romfs_fs);
|
||||
|
||||
MODULE_DESCRIPTION("Direct-MTD Capable RomFS");
|
||||
MODULE_AUTHOR("Red Hat, Inc.");
|
||||
MODULE_LICENSE("GPL"); /* Actually dual-licensed, but it doesn't matter for */
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user