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:
Linus Torvalds 2009-04-06 14:56:26 -07:00
commit 22ae77bc7a
78 changed files with 4080 additions and 996 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
View 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;

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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) {

View file

@ -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

View file

@ -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);

View 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");

View file

@ -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;

View file

@ -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 <[email protected]>)");
MODULE_DESCRIPTION("MTD map driver for SHARP SL series");

View file

@ -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
View 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),
};

View file

@ -13,39 +13,13 @@
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/smp_lock.h>
#include <linux/backing-dev.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
#include <asm/uaccess.h>
static struct class *mtd_class;
static void mtd_notify_add(struct mtd_info* mtd)
{
if (!mtd)
return;
device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
NULL, "mtd%d", mtd->index);
device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
NULL, "mtd%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
{
if (!mtd)
return;
device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
}
static struct mtd_notifier notifier = {
.add = mtd_notify_add,
.remove = mtd_notify_remove,
};
/*
* Data structure to hold the pointer to the mtd device as well
@ -107,12 +81,15 @@ static int mtd_open(struct inode *inode, struct file *file)
goto out;
}
if (MTD_ABSENT == mtd->type) {
if (mtd->type == MTD_ABSENT) {
put_mtd_device(mtd);
ret = -ENODEV;
goto out;
}
if (mtd->backing_dev_info)
file->f_mapping->backing_dev_info = mtd->backing_dev_info;
/* You can't open it RW if it's not a writeable device */
if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
put_mtd_device(mtd);
@ -781,6 +758,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
return ret;
} /* memory_ioctl */
/*
* try to determine where a shared mapping can be made
* - only supported for NOMMU at the moment (MMU can't doesn't copy private
* mappings)
*/
#ifndef CONFIG_MMU
static unsigned long mtd_get_unmapped_area(struct file *file,
unsigned long addr,
unsigned long len,
unsigned long pgoff,
unsigned long flags)
{
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
if (mtd->get_unmapped_area) {
unsigned long offset;
if (addr != 0)
return (unsigned long) -EINVAL;
if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
return (unsigned long) -EINVAL;
offset = pgoff << PAGE_SHIFT;
if (offset > mtd->size - len)
return (unsigned long) -EINVAL;
return mtd->get_unmapped_area(mtd, len, offset, flags);
}
/* can't map directly */
return (unsigned long) -ENOSYS;
}
#endif
/*
* set up a mapping for shared memory segments
*/
static int mtd_mmap(struct file *file, struct vm_area_struct *vma)
{
#ifdef CONFIG_MMU
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM)
return 0;
return -ENOSYS;
#else
return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
#endif
}
static const struct file_operations mtd_fops = {
.owner = THIS_MODULE,
.llseek = mtd_lseek,
@ -789,39 +819,36 @@ static const struct file_operations mtd_fops = {
.ioctl = mtd_ioctl,
.open = mtd_open,
.release = mtd_close,
.mmap = mtd_mmap,
#ifndef CONFIG_MMU
.get_unmapped_area = mtd_get_unmapped_area,
#endif
};
static int __init init_mtdchar(void)
{
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
int status;
status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);
if (status < 0) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
mtd_class = class_create(THIS_MODULE, "mtd");
if (IS_ERR(mtd_class)) {
printk(KERN_ERR "Error creating mtd class.\n");
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
return PTR_ERR(mtd_class);
}
register_mtd_user(&notifier);
return 0;
return status;
}
static void __exit cleanup_mtdchar(void)
{
unregister_mtd_user(&notifier);
class_destroy(mtd_class);
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
}
module_init(init_mtdchar);
module_exit(cleanup_mtdchar);
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <[email protected]>");
MODULE_DESCRIPTION("Direct character-device access to MTD devices");
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);

View file

@ -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:

View file

@ -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 <[email protected]>");
MODULE_DESCRIPTION("Core MTD registration and access routines");

View file

@ -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);
}

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View 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");

View file

@ -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);

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View 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");

View 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");

View file

@ -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 <[email protected]>, Fabrice Bellard <[email protected]> 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);

View file

@ -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);

View file

@ -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;

View file

@ -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;
}