diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/chips/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mtd/devices/Kconfig | 17 | ||||
-rw-r--r-- | drivers/mtd/devices/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2000.c | 2 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2001.c | 2 | ||||
-rw-r--r-- | drivers/mtd/devices/doc2001plus.c | 2 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 582 | ||||
-rw-r--r-- | drivers/mtd/devices/mtd_dataflash.c | 629 | ||||
-rw-r--r-- | drivers/mtd/maps/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mtd/maps/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/maps/epxa10db-flash.c | 179 | ||||
-rw-r--r-- | drivers/mtd/maps/pcmciamtd.c | 121 | ||||
-rw-r--r-- | drivers/mtd/mtd_blkdevs.c | 25 | ||||
-rw-r--r-- | drivers/mtd/nand/au1550nd.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/diskonchip.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/rtc_from4.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/spia.c | 2 | ||||
-rw-r--r-- | drivers/mtd/onenand/generic.c | 5 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 53 | ||||
-rw-r--r-- | drivers/mtd/onenand/onenand_bbt.c | 4 | ||||
-rw-r--r-- | drivers/mtd/rfd_ftl.c | 1 |
22 files changed, 1334 insertions, 320 deletions
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index eafa23f5cbd..effa0d7a73a 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -31,6 +31,7 @@ config MTD_JEDECPROBE config MTD_GEN_PROBE tristate + select OBSOLETE_INTERMODULE config MTD_CFI_ADV_OPTIONS bool "Flash chip driver advanced configuration options" @@ -259,7 +260,7 @@ config MTD_ABSENT with this driver will return -ENODEV upon access. config MTD_OBSOLETE_CHIPS - depends on MTD && BROKEN + depends on MTD bool "Older (theoretically obsoleted now) drivers for non-CFI chips" help This option does not enable any code directly, but will allow you to @@ -272,7 +273,7 @@ config MTD_OBSOLETE_CHIPS config MTD_AMDSTD tristate "AMD compatible flash chip support (non-CFI)" - depends on MTD && MTD_OBSOLETE_CHIPS + depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN help This option enables support for flash chips using AMD-compatible commands, including some which are not CFI-compatible and hence @@ -290,7 +291,7 @@ config MTD_SHARP config MTD_JEDEC tristate "JEDEC device support" - depends on MTD && MTD_OBSOLETE_CHIPS + depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN help Enable older older JEDEC flash interface devices for self programming flash. It is commonly used in older AMD chips. It is diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 9a2aa4033c6..dd628cb51e3 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -47,6 +47,22 @@ config MTD_MS02NV accelerator. Say Y here if you have a DECstation 5000/2x0 or a DECsystem 5900 equipped with such a module. +config MTD_DATAFLASH + tristate "Support for AT45xxx DataFlash" + depends on MTD && SPI_MASTER && EXPERIMENTAL + help + This enables access to AT45xxx DataFlash chips, using SPI. + Sometimes DataFlash chips are packaged inside MMC-format + cards; at this writing, the MMC stack won't handle those. + +config MTD_M25P80 + tristate "Support for M25 SPI Flash" + depends on MTD && SPI_MASTER && EXPERIMENTAL + help + This enables access to ST M25P80 and similar SPI flash chips, + used for program and data storage. Set up your spi devices + with the right board-specific platform data. + config MTD_SLRAM tristate "Uncached system RAM" depends on MTD @@ -202,6 +218,7 @@ config MTD_DOC2001PLUS config MTD_DOCPROBE tristate select MTD_DOCECC + select OBSOLETE_INTERMODULE config MTD_DOCECC tristate diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index e38db348057..7c5ed217838 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -23,3 +23,5 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o +obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o +obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index be5e88b3888..e4345cf744a 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -138,7 +138,7 @@ static inline int DoC_WaitReady(struct DiskOnChip *doc) bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, +static int DoC_Command(struct DiskOnChip *doc, unsigned char command, unsigned char xtraflags) { void __iomem *docptr = doc->virtadr; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index fcb28a6fd89..681a9c73a2a 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -103,7 +103,7 @@ static inline int DoC_WaitReady(void __iomem * docptr) with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */ -static inline void DoC_Command(void __iomem * docptr, unsigned char command, +static void DoC_Command(void __iomem * docptr, unsigned char command, unsigned char xtraflags) { /* Assert the CLE (Command Latch Enable) line to the flash chip */ diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 0595cc7324b..5f57f29efee 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -118,7 +118,7 @@ static inline void DoC_CheckASIC(void __iomem * docptr) /* DoC_Command: Send a flash command to the flash chip through the Flash * command register. Need 2 Write Pipeline Terminates to complete send. */ -static inline void DoC_Command(void __iomem * docptr, unsigned char command, +static void DoC_Command(void __iomem * docptr, unsigned char command, unsigned char xtraflags) { WriteDOC(command, docptr, Mplus_FlashCmd); diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c new file mode 100644 index 00000000000..d5f24089be7 --- /dev/null +++ b/drivers/mtd/devices/m25p80.c @@ -0,0 +1,582 @@ +/* + * MTD SPI driver for ST M25Pxx flash chips + * + * Author: Mike Lavender, mike@steroidmicros.com + * + * Copyright (c) 2005, Intec Automation Inc. + * + * Some parts are based on lart.c by Abraham Van Der Merwe + * + * Cleaned up and generalized based on mtd_dataflash.c + * + * This code 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/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/interrupt.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> + +#include <asm/semaphore.h> + + +/* NOTE: AT 25F and SST 25LF series are very similar, + * but commands for sector erase and chip id differ... + */ + +#define FLASH_PAGESIZE 256 + +/* Flash opcodes. */ +#define OPCODE_WREN 6 /* Write enable */ +#define OPCODE_RDSR 5 /* Read status register */ +#define OPCODE_READ 3 /* Read data bytes */ +#define OPCODE_PP 2 /* Page program */ +#define OPCODE_SE 0xd8 /* Sector erase */ +#define OPCODE_RES 0xab /* Read Electronic Signature */ +#define OPCODE_RDID 0x9f /* Read JEDEC ID */ + +/* Status Register bits. */ +#define SR_WIP 1 /* Write in progress */ +#define SR_WEL 2 /* Write enable latch */ +#define SR_BP0 4 /* Block protect 0 */ +#define SR_BP1 8 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_SRWD 0x80 /* SR write protect */ + +/* Define max times to check status register before we give up. */ +#define MAX_READY_WAIT_COUNT 100000 + + +#ifdef CONFIG_MTD_PARTITIONS +#define mtd_has_partitions() (1) +#else +#define mtd_has_partitions() (0) +#endif + +/****************************************************************************/ + +struct m25p { + struct spi_device *spi; + struct semaphore lock; + struct mtd_info mtd; + unsigned partitioned; + u8 command[4]; +}; + +static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) +{ + return container_of(mtd, struct m25p, mtd); +} + +/****************************************************************************/ + +/* + * Internal helper functions + */ + +/* + * Read the status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_sr(struct m25p *flash) +{ + ssize_t retval; + u8 code = OPCODE_RDSR; + u8 val; + + retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); + + if (retval < 0) { + dev_err(&flash->spi->dev, "error %d reading SR\n", + (int) retval); + return retval; + } + + return val; +} + + +/* + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static inline int write_enable(struct m25p *flash) +{ + u8 code = OPCODE_WREN; + + return spi_write_then_read(flash->spi, &code, 1, NULL, 0); +} + + +/* + * Service routine to read status register until ready, or timeout occurs. + * Returns non-zero if error. + */ +static int wait_till_ready(struct m25p *flash) +{ + int count; + int sr; + + /* one chip guarantees max 5 msec wait here after page writes, + * but potentially three seconds (!) after page erase. + */ + for (count = 0; count < MAX_READY_WAIT_COUNT; count++) { + if ((sr = read_sr(flash)) < 0) + break; + else if (!(sr & SR_WIP)) + return 0; + + /* REVISIT sometimes sleeping would be best */ + } + + return 1; +} + + +/* + * Erase one sector of flash memory at offset ``offset'' which is any + * address within the sector which should be erased. + * + * Returns 0 if successful, non-zero otherwise. + */ +static int erase_sector(struct m25p *flash, u32 offset) +{ + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id, + __FUNCTION__, offset); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + return 1; + + /* Send write enable, then erase commands. */ + write_enable(flash); + + /* Set up command buffer. */ + flash->command[0] = OPCODE_SE; + flash->command[1] = offset >> 16; + flash->command[2] = offset >> 8; + flash->command[3] = offset; + + spi_write(flash->spi, flash->command, sizeof(flash->command)); + + return 0; +} + +/****************************************************************************/ + +/* + * MTD implementation + */ + +/* + * Erase an address range on the flash chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct m25p *flash = mtd_to_m25p(mtd); + u32 addr,len; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", + flash->spi->dev.bus_id, __FUNCTION__, "at", + (u32)instr->addr, instr->len); + + /* sanity checks */ + if (instr->addr + instr->len > flash->mtd.size) + return -EINVAL; + if ((instr->addr % mtd->erasesize) != 0 + || (instr->len % mtd->erasesize) != 0) { + return -EINVAL; + } + + addr = instr->addr; + len = instr->len; + + down(&flash->lock); + + /* now erase those sectors */ + while (len) { + if (erase_sector(flash, addr)) { + instr->state = MTD_ERASE_FAILED; + up(&flash->lock); + return -EIO; + } + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + + up(&flash->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +/* + * Read an address range from the flash chip. The address range + * may be any size provided it is within the physical boundaries. + */ +static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct m25p *flash = mtd_to_m25p(mtd); + struct spi_transfer t[2]; + struct spi_message m; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", + flash->spi->dev.bus_id, __FUNCTION__, "from", + (u32)from, len); + + /* sanity checks */ + if (!len) + return 0; + + if (from + len > flash->mtd.size) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + t[0].tx_buf = flash->command; + t[0].len = sizeof(flash->command); + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = buf; + t[1].len = len; + spi_message_add_tail(&t[1], &m); + + /* Byte count starts at zero. */ + if (retlen) + *retlen = 0; + + down(&flash->lock); + + /* Wait till previous write/erase is done. */ + if (wait_till_ready(flash)) { + /* REVISIT status return?? */ + up(&flash->lock); + return 1; + } + + /* NOTE: OPCODE_FAST_READ (if available) is faster... */ + + /* Set up the write data buffer. */ + flash->command[0] = OPCODE_READ; + flash->command[1] = from >> 16; + flash->command[2] = from >> 8; + flash->command[3] = from; + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - sizeof(flash->command); + + up(&flash->lock); + + return 0; +} + +/* + * Write an address range to the flash chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. + */ +static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct m25p *flash = mtd_to_m25p(mtd); + u32 page_offset, page_size; + struct spi_transfer t[2]; + struct spi_message m; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n", + flash->spi->dev.bus_id, __FUNCTION__, "to", + (u32)to, len); + + if (retlen) + *retlen = 0; + + /* sanity checks */ + if (!len) + return(0); + + if (to + len > flash->mtd.size) + return -EINVAL; + + spi_message_init(&m); + memset(t, 0, (sizeof t)); + + t[0].tx_buf = flash->command; + t[0].len = sizeof(flash->command); + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = buf; + spi_message_add_tail(&t[1], &m); + + down(&flash->lock); + + /* Wait until finished previous write command. */ + if (wait_till_ready(flash)) + return 1; + + write_enable(flash); + + /* Set up the opcode in the write buffer. */ + flash->command[0] = OPCODE_PP; + flash->command[1] = to >> 16; + flash->command[2] = to >> 8; + flash->command[3] = to; + + /* what page do we start with? */ + page_offset = to % FLASH_PAGESIZE; + + /* do all the bytes fit onto one page? */ + if (page_offset + len <= FLASH_PAGESIZE) { + t[1].len = len; + + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - sizeof(flash->command); + } else { + u32 i; + + /* the size of data remaining on the first page */ + page_size = FLASH_PAGESIZE - page_offset; + + t[1].len = page_size; + spi_sync(flash->spi, &m); + + *retlen = m.actual_length - sizeof(flash->command); + + /* write everything in PAGESIZE chunks */ + for (i = page_size; i < len; i += page_size) { + page_size = len - i; + if (page_size > FLASH_PAGESIZE) + page_size = FLASH_PAGESIZE; + + /* write the next page to flash */ + flash->command[1] = (to + i) >> 16; + flash->command[2] = (to + i) >> 8; + flash->command[3] = (to + i); + + t[1].tx_buf = buf + i; + t[1].len = page_size; + + wait_till_ready(flash); + + write_enable(flash); + + spi_sync(flash->spi, &m); + + if (retlen) + *retlen += m.actual_length + - sizeof(flash->command); + } + } + + up(&flash->lock); + + return 0; +} + + +/****************************************************************************/ + +/* + * SPI device driver setup and teardown + */ + +struct flash_info { + char *name; + u8 id; + u16 jedec_id; + unsigned sector_size; + unsigned n_sectors; +}; + +static struct flash_info __devinitdata m25p_data [] = { + /* REVISIT: fill in JEDEC ids, for parts that have them */ + { "m25p05", 0x05, 0x0000, 32 * 1024, 2 }, + { "m25p10", 0x10, 0x0000, 32 * 1024, 4 }, + { "m25p20", 0x11, 0x0000, 64 * 1024, 4 }, + { "m25p40", 0x12, 0x0000, 64 * 1024, 8 }, + { "m25p80", 0x13, 0x0000, 64 * 1024, 16 }, + { "m25p16", 0x14, 0x0000, 64 * 1024, 32 }, + { "m25p32", 0x15, 0x0000, 64 * 1024, 64 }, + { "m25p64", 0x16, 0x2017, 64 * 1024, 128 }, +}; + +/* + * board specific setup should have ensured the SPI clock used here + * matches what the READ command supports, at least until this driver + * understands FAST_READ (for clocks over 25 MHz). + */ +static int __devinit m25p_probe(struct spi_device *spi) +{ + struct flash_platform_data *data; + struct m25p *flash; + struct flash_info *info; + unsigned i; + + /* Platform data helps sort out which chip type we have, as + * well as how this board partitions it. + */ + data = spi->dev.platform_data; + if (!data || !data->type) { + /* FIXME some chips can identify themselves with RES + * or JEDEC get-id commands. Try them ... + */ + DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", + flash->spi->dev.bus_id); + return -ENODEV; + } + + for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) { + if (strcmp(data->type, info->name) == 0) + break; + } + if (i == ARRAY_SIZE(m25p_data)) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n", + flash->spi->dev.bus_id, data->type); + return -ENODEV; + } + + flash = kzalloc(sizeof *flash, SLAB_KERNEL); + if (!flash) + return -ENOMEM; + + flash->spi = spi; + init_MUTEX(&flash->lock); + dev_set_drvdata(&spi->dev, flash); + + if (data->name) + flash->mtd.name = data->name; + else + flash->mtd.name = spi->dev.bus_id; + + flash->mtd.type = MTD_NORFLASH; + flash->mtd.flags = MTD_CAP_NORFLASH; + flash->mtd.size = info->sector_size * info->n_sectors; + flash->mtd.erasesize = info->sector_size; + flash->mtd.erase = m25p80_erase; + flash->mtd.read = m25p80_read; + flash->mtd.write = m25p80_write; + + dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, + flash->mtd.size / 1024); + + DEBUG(MTD_DEBUG_LEVEL2, + "mtd .name = %s, .size = 0x%.8x (%uM) " + ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n", + flash->mtd.name, + flash->mtd.size, flash->mtd.size / (1024*1024), + flash->mtd.erasesize, flash->mtd.erasesize / 1024, + flash->mtd.numeraseregions); + + if (flash->mtd.numeraseregions) + for (i = 0; i < flash->mtd.numeraseregions; i++) + DEBUG(MTD_DEBUG_LEVEL2, + "mtd.eraseregions[%d] = { .offset = 0x%.8x, " + ".erasesize = 0x%.8x (%uK), " + ".numblocks = %d }\n", + i, flash->mtd.eraseregions[i].offset, + flash->mtd.eraseregions[i].erasesize, + flash->mtd.eraseregions[i].erasesize / 1024, + flash->mtd.eraseregions[i].numblocks); + + + /* partitions should match sector boundaries; and it may be good to + * use readonly partitions for writeprotected sectors (BP2..BP0). + */ + if (mtd_has_partitions()) { + struct mtd_partition *parts = NULL; + int nr_parts = 0; + +#ifdef CONFIG_MTD_CMDLINE_PARTS + static const char *part_probes[] = { "cmdlinepart", NULL, }; + + nr_parts = parse_mtd_partitions(&flash->mtd, + part_probes, &parts, 0); +#endif + + if (nr_parts <= 0 && data && data->parts) { + parts = data->parts; + nr_parts = data->nr_parts; + } + + if (nr_parts > 0) { + for (i = 0; i < data->nr_parts; i++) { + DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " + "{.name = %s, .offset = 0x%.8x, " + ".size = 0x%.8x (%uK) }\n", + i, data->parts[i].name, + data->parts[i].offset, + data->parts[i].size, + data->parts[i].size / 1024); + } + flash->partitioned = 1; + return add_mtd_partitions(&flash->mtd, parts, nr_parts); + } + } else if (data->nr_parts) + dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", + data->nr_parts, data->name); + + return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0; +} + + +static int __devexit m25p_remove(struct spi_device *spi) +{ + struct m25p *flash = dev_get_drvdata(&spi->dev); + int status; + + /* Clean up MTD stuff. */ + if (mtd_has_partitions() && flash->partitioned) + status = del_mtd_partitions(&flash->mtd); + else + status = del_mtd_device(&flash->mtd); + if (status == 0) + kfree(flash); + return 0; +} + + +static struct spi_driver m25p80_driver = { + .driver = { + .name = "m25p80", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = m25p_probe, + .remove = __devexit_p(m25p_remove), +}; + + +static int m25p80_init(void) +{ + return spi_register_driver(&m25p80_driver); +} + + +static void m25p80_exit(void) +{ + spi_unregister_driver(&m25p80_driver); +} + + +module_init(m25p80_init); +module_exit(m25p80_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Lavender"); +MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips"); diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c new file mode 100644 index 00000000000..155737e7483 --- /dev/null +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -0,0 +1,629 @@ +/* + * Atmel AT45xxx DataFlash MTD driver for lightweight SPI framework + * + * Largely derived from at91_dataflash.c: + * Copyright (C) 2003-2005 SAN People (Pty) Ltd + * + * 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/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/spi/spi.h> +#include <linux/spi/flash.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + + +/* + * DataFlash is a kind of SPI flash. Most AT45 chips have two buffers in + * each chip, which may be used for double buffered I/O; but this driver + * doesn't (yet) use these for any kind of i/o overlap or prefetching. + * + * Sometimes DataFlash is packaged in MMC-format cards, although the + * MMC stack can't use SPI (yet), or distinguish between MMC and DataFlash + * protocols during enumeration. + */ + +#define CONFIG_DATAFLASH_WRITE_VERIFY + +/* reads can bypass the buffers */ +#define OP_READ_CONTINUOUS 0xE8 +#define OP_READ_PAGE 0xD2 + +/* group B requests can run even while status reports "busy" */ +#define OP_READ_STATUS 0xD7 /* group B */ + +/* move data between host and buffer */ +#define OP_READ_BUFFER1 0xD4 /* group B */ +#define OP_READ_BUFFER2 0xD6 /* group B */ +#define OP_WRITE_BUFFER1 0x84 /* group B */ +#define OP_WRITE_BUFFER2 0x87 /* group B */ + +/* erasing flash */ +#define OP_ERASE_PAGE 0x81 +#define OP_ERASE_BLOCK 0x50 + +/* move data between buffer and flash */ +#define OP_TRANSFER_BUF1 0x53 +#define OP_TRANSFER_BUF2 0x55 +#define OP_MREAD_BUFFER1 0xD4 +#define OP_MREAD_BUFFER2 0xD6 +#define OP_MWERASE_BUFFER1 0x83 +#define OP_MWERASE_BUFFER2 0x86 +#define OP_MWRITE_BUFFER1 0x88 /* sector must be pre-erased */ +#define OP_MWRITE_BUFFER2 0x89 /* sector must be pre-erased */ + +/* write to buffer, then write-erase to flash */ +#define OP_PROGRAM_VIA_BUF1 0x82 +#define OP_PROGRAM_VIA_BUF2 0x85 + +/* compare buffer to flash */ +#define OP_COMPARE_BUF1 0x60 +#define OP_COMPARE_BUF2 0x61 + +/* read flash to buffer, then write-erase to flash */ +#define OP_REWRITE_VIA_BUF1 0x58 +#define OP_REWRITE_VIA_BUF2 0x59 + +/* newer chips report JEDEC manufacturer and device IDs; chip + * serial number and OTP bits; and per-sector writeprotect. + */ +#define OP_READ_ID 0x9F +#define OP_READ_SECURITY 0x77 +#define OP_WRITE_SECURITY 0x9A /* OTP bits */ + + +struct dataflash { + u8 command[4]; + char name[24]; + + unsigned partitioned:1; + + unsigned short page_offset; /* offset in flash address */ + unsigned int page_size; /* of bytes per page */ + + struct semaphore lock; + struct spi_device *spi; + + struct mtd_info mtd; +}; + +#ifdef CONFIG_MTD_PARTITIONS +#define mtd_has_partitions() (1) +#else +#define mtd_has_partitions() (0) +#endif + +/* ......................................................................... */ + +/* + * Return the status of the DataFlash device. + */ +static inline int dataflash_status(struct spi_device *spi) +{ + /* NOTE: at45db321c over 25 MHz wants to write + * a dummy byte after the opcode... + */ + return spi_w8r8(spi, OP_READ_STATUS); +} + +/* + * Poll the DataFlash device until it is READY. + * This usually takes 5-20 msec or so; more for sector erase. + */ +static int dataflash_waitready(struct spi_device *spi) +{ + int status; + + for (;;) { + status = dataflash_status(spi); + if (status < 0) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: status %d?\n", + spi->dev.bus_id, status); + status = 0; + } + + if (status & (1 << 7)) /* RDY/nBSY */ + return status; + + msleep(3); + } +} + +/* ......................................................................... */ + +/* + * Erase pages of flash. + */ +static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct dataflash *priv = (struct dataflash *)mtd->priv; + struct spi_device *spi = priv->spi; + struct spi_transfer x = { .tx_dma = 0, }; + struct spi_message msg; + unsigned blocksize = priv->page_size << 3; + u8 *command; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: erase addr=0x%x len 0x%x\n", + spi->dev.bus_id, + instr->addr, instr->len); + + /* Sanity checks */ + if ((instr->addr + instr->len) > mtd->size + || (instr->len % priv->page_size) != 0 + || (instr->addr % priv->page_size) != 0) + return -EINVAL; + + spi_message_init(&msg); + + x.tx_buf = command = priv->command; + x.len = 4; + spi_message_add_tail(&x, &msg); + + down(&priv->lock); + while (instr->len > 0) { + unsigned int pageaddr; + int status; + int do_block; + + /* Calculate flash page address; use block erase (for speed) if + * we're at a block boundary and need to erase the whole block. + */ + pageaddr = instr->addr / priv->page_size; + do_block = (pageaddr & 0x7) == 0 && instr->len <= blocksize; + pageaddr = pageaddr << priv->page_offset; + + command[0] = do_block ? OP_ERASE_BLOCK : OP_ERASE_PAGE; + command[1] = (u8)(pageaddr >> 16); + command[2] = (u8)(pageaddr >> 8); + command[3] = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "ERASE %s: (%x) %x %x %x [%i]\n", + do_block ? "block" : "page", + command[0], command[1], command[2], command[3], + pageaddr); + + status = spi_sync(spi, &msg); + (void) dataflash_waitready(spi); + + if (status < 0) { + printk(KERN_ERR "%s: erase %x, err %d\n", + spi->dev.bus_id, pageaddr, status); + /* REVISIT: can retry instr->retries times; or + * giveup and instr->fail_addr = instr->addr; + */ + continue; + } + + if (do_block) { + instr->addr += blocksize; + instr->len -= blocksize; + } else { + instr->addr += priv->page_size; + instr->len -= priv->page_size; + } + } + up(&priv->lock); + + /* Inform MTD subsystem that erase is complete */ + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + +/* + * Read from the DataFlash device. + * from : Start offset in flash device + * len : Amount to read + * retlen : About of data actually read + * buf : Buffer containing the data + */ +static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct dataflash *priv = (struct dataflash *)mtd->priv; + struct spi_transfer x[2] = { { .tx_dma = 0, }, }; + struct spi_message msg; + unsigned int addr; + u8 *command; + int status; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: read 0x%x..0x%x\n", + priv->spi->dev.bus_id, (unsigned)from, (unsigned)(from + len)); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (from + len > mtd->size) + return -EINVAL; + + /* Calculate flash page/byte address */ + addr = (((unsigned)from / priv->page_size) << priv->page_offset) + + ((unsigned)from % priv->page_size); + + command = priv->command; + + DEBUG(MTD_DEBUG_LEVEL3, "READ: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + spi_message_init(&msg); + + x[0].tx_buf = command; + x[0].len = 8; + spi_message_add_tail(&x[0], &msg); + + x[1].rx_buf = buf; + x[1].len = len; + spi_message_add_tail(&x[1], &msg); + + down(&priv->lock); + + /* Continuous read, max clock = f(car) which may be less than + * the peak rate available. Some chips support commands with + * fewer "don't care" bytes. Both buffers stay unchanged. + */ + command[0] = OP_READ_CONTINUOUS; + command[1] = (u8)(addr >> 16); + command[2] = (u8)(addr >> 8); + command[3] = (u8)(addr >> 0); + /* plus 4 "don't care" bytes */ + + status = spi_sync(priv->spi, &msg); + up(&priv->lock); + + if (status >= 0) { + *retlen = msg.actual_length - 8; + status = 0; + } else + DEBUG(MTD_DEBUG_LEVEL1, "%s: read %x..%x --> %d\n", + priv->spi->dev.bus_id, + (unsigned)from, (unsigned)(from + len), + status); + return status; +} + +/* + * Write to the DataFlash device. + * to : Start offset in flash device + * len : Amount to write + * retlen : Amount of data actually written + * buf : Buffer containing the data + */ +static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct dataflash *priv = (struct dataflash *)mtd->priv; + struct spi_device *spi = priv->spi; + struct spi_transfer x[2] = { { .tx_dma = 0, }, }; + struct spi_message msg; + unsigned int pageaddr, addr, offset, writelen; + size_t remaining = len; + u_char *writebuf = (u_char *) buf; + int status = -EINVAL; + u8 *command; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: write 0x%x..0x%x\n", + spi->dev.bus_id, (unsigned)to, (unsigned)(to + len)); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if ((to + len) > mtd->size) + return -EINVAL; + + spi_message_init(&msg); + + x[0].tx_buf = command = priv->command; + x[0].len = 4; + spi_message_add_tail(&x[0], &msg); + + pageaddr = ((unsigned)to / priv->page_size); + offset = ((unsigned)to % priv->page_size); + if (offset + len > priv->page_size) + writelen = priv->page_size - offset; + else + writelen = len; + + down(&priv->lock); + while (remaining > 0) { + DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", + pageaddr, offset, writelen); + + /* REVISIT: + * (a) each page in a sector must be rewritten at least + * once every 10K sibling erase/program operations. + * (b) for pages that are already erased, we could + * use WRITE+MWRITE not PROGRAM for ~30% speedup. + * (c) WRITE to buffer could be done while waiting for + * a previous MWRITE/MWERASE to complete ... + * (d) error handling here seems to be mostly missing. + * + * Two persistent bits per page, plus a per-sector counter, + * could support (a) and (b) ... we might consider using + * the second half of sector zero, which is just one block, + * to track that state. (On AT91, that sector should also + * support boot-from-DataFlash.) + */ + + addr = pageaddr << priv->page_offset; + + /* (1) Maybe transfer partial page to Buffer1 */ + if (writelen != priv->page_size) { + command[0] = OP_TRANSFER_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "TRANSFER: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + status = spi_sync(spi, &msg); + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: xfer %u -> %d \n", + spi->dev.bus_id, addr, status); + + (void) dataflash_waitready(priv->spi); + } + + /* (2) Program full page via Buffer1 */ + addr += offset; + command[0] = OP_PROGRAM_VIA_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = (addr & 0x000000FF); + + DEBUG(MTD_DEBUG_LEVEL3, "PROGRAM: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + x[1].tx_buf = writebuf; + x[1].len = writelen; + spi_message_add_tail(x + 1, &msg); + status = spi_sync(spi, &msg); + spi_transfer_del(x + 1); + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: pgm %u/%u -> %d \n", + spi->dev.bus_id, addr, writelen, status); + + (void) dataflash_waitready(priv->spi); + + +#ifdef CONFIG_DATAFLASH_WRITE_VERIFY + + /* (3) Compare to Buffer1 */ + addr = pageaddr << priv->page_offset; + command[0] = OP_COMPARE_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; + + DEBUG(MTD_DEBUG_LEVEL3, "COMPARE: (%x) %x %x %x\n", + command[0], command[1], command[2], command[3]); + + status = spi_sync(spi, &msg); + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: compare %u -> %d \n", + spi->dev.bus_id, addr, status); + + status = dataflash_waitready(priv->spi); + + /* Check result of the compare operation */ + if ((status & (1 << 6)) == 1) { + printk(KERN_ERR "%s: compare page %u, err %d\n", + spi->dev.bus_id, pageaddr, status); + remaining = 0; + status = -EIO; + break; + } else + status = 0; + +#endif /* CONFIG_DATAFLASH_WRITE_VERIFY */ + + remaining = remaining - writelen; + pageaddr++; + offset = 0; + writebuf += writelen; + *retlen += writelen; + + if (remaining > priv->page_size) + writelen = priv->page_size; + else + writelen = remaining; + } + up(&priv->lock); + + return status; +} + +/* ......................................................................... */ + +/* + * Register DataFlash device with MTD subsystem. + */ +static int __devinit +add_dataflash(struct spi_device *spi, char *name, + int nr_pages, int pagesize, int pageoffset) +{ + struct dataflash *priv; + struct mtd_info *device; + struct flash_platform_data *pdata = spi->dev.platform_data; + + priv = (struct dataflash *) kzalloc(sizeof *priv, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + init_MUTEX(&priv->lock); + priv->spi = spi; + priv->page_size = pagesize; + priv->page_offset = pageoffset; + + /* name must be usable with cmdlinepart */ + sprintf(priv->name, "spi%d.%d-%s", + spi->master->bus_num, spi->chip_select, + name); + + device = &priv->mtd; + device->name = (pdata && pdata->name) ? pdata->name : priv->name; + device->size = nr_pages * pagesize; + device->erasesize = pagesize; + device->owner = THIS_MODULE; + device->type = MTD_DATAFLASH; + device->flags = MTD_CAP_NORFLASH; + device->erase = dataflash_erase; + device->read = dataflash_read; + device->write = dataflash_write; + device->priv = priv; + + dev_info(&spi->dev, "%s (%d KBytes)\n", name, device->size/1024); + dev_set_drvdata(&spi->dev, priv); + + if (mtd_has_partitions()) { + struct mtd_partition *parts; + int nr_parts = 0; + +#ifdef CONFIG_MTD_CMDLINE_PARTS + static const char *part_probes[] = { "cmdlinepart", NULL, }; + + nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0); +#endif + + if (nr_parts <= 0 && pdata && pdata->parts) { + parts = pdata->parts; + nr_parts = pdata->nr_parts; + } + + if (nr_parts > 0) { + priv->partitioned = 1; + return add_mtd_partitions(device, parts, nr_parts); + } + } else if (pdata && pdata->nr_parts) + dev_warn(&spi->dev, "ignoring %d default partitions on %s\n", + pdata->nr_parts, device->name); + + return add_mtd_device(device) == 1 ? -ENODEV : 0; +} + +/* + * Detect and initialize DataFlash device: + * + * Device Density ID code #Pages PageSize Offset + * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9 + * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9 + * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9 + * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9 + * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10 + * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10 + * AT45DB0642 64Mbit (8M) xx111xxx (0x3c) 8192 1056 11 + * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 + */ +static int __devinit dataflash_probe(struct spi_device *spi) +{ + int status; + + status = dataflash_status(spi); + if (status <= 0 || status == 0xff) { + DEBUG(MTD_DEBUG_LEVEL1, "%s: status error %d\n", + spi->dev.bus_id, status); + if (status == 0xff) + status = -ENODEV; + return status; + } + + /* if there's a device there, assume it's dataflash. + * board setup should have set spi->max_speed_max to + * match f(car) for continuous reads, mode 0 or 3. + */ + switch (status & 0x3c) { + case 0x0c: /* 0 0 1 1 x x */ + status = add_dataflash(spi, "AT45DB011B", 512, 264, 9); + break; + case 0x14: /* 0 1 0 1 x x */ + status = add_dataflash(spi, "AT45DB021B", 1025, 264, 9); + break; + case 0x1c: /* 0 1 1 1 x x */ + status = add_dataflash(spi, "AT45DB041x", 2048, 264, 9); + break; + case 0x24: /* 1 0 0 1 x x */ + status = add_dataflash(spi, "AT45DB081B", 4096, 264, 9); + break; + case 0x2c: /* 1 0 1 1 x x */ + status = add_dataflash(spi, "AT45DB161x", 4096, 528, 10); + break; + case 0x34: /* 1 1 0 1 x x */ + status = add_dataflash(spi, "AT45DB321x", 8192, 528, 10); + break; + case 0x38: /* 1 1 1 x x x */ + case 0x3c: + status = add_dataflash(spi, "AT45DB642x", 8192, 1056, 11); + break; + /* obsolete AT45DB1282 not (yet?) supported */ + default: + DEBUG(MTD_DEBUG_LEVEL1, "%s: unsupported device (%x)\n", + spi->dev.bus_id, status & 0x3c); + status = -ENODEV; + } + + if (status < 0) + DEBUG(MTD_DEBUG_LEVEL1, "%s: add_dataflash --> %d\n", + spi->dev.bus_id, status); + + return status; +} + +static int __devexit dataflash_remove(struct spi_device *spi) +{ + struct dataflash *flash = dev_get_drvdata(&spi->dev); + int status; + + DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", spi->dev.bus_id); + + if (mtd_has_partitions() && flash->partitioned) + status = del_mtd_partitions(&flash->mtd); + else + status = del_mtd_device(&flash->mtd); + if (status == 0) + kfree(flash); + return status; +} + +static struct spi_driver dataflash_driver = { + .driver = { + .name = "mtd_dataflash", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = dataflash_probe, + .remove = __devexit_p(dataflash_remove), + + /* FIXME: investigate suspend and resume... */ +}; + +static int __init dataflash_init(void) +{ + return spi_register_driver(&dataflash_driver); +} +module_init(dataflash_init); + +static void __exit dataflash_exit(void) +{ + spi_unregister_driver(&dataflash_driver); +} +module_exit(dataflash_exit); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrew Victor, David Brownell"); +MODULE_DESCRIPTION("MTD DataFlash driver"); diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 452ccd5037c..7abd7fee0dd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -62,7 +62,7 @@ config MTD_PHYSMAP_BANKWIDTH config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" - depends on (SPARC32 || SPARC64) && MTD_CFI + depends on SPARC && MTD_CFI help This provides a 'mapping' driver which supports the way in which user-programmable flash chips are connected on various @@ -473,14 +473,6 @@ config MTD_IXP2000 IXDP425 and Coyote. If you have an IXP2000 based board and would like to use the flash chips on it, say 'Y'. -config MTD_EPXA10DB - tristate "CFI Flash device mapped on Epxa10db" - depends on MTD_CFI && MTD_PARTITIONS && ARCH_CAMELOT - help - This enables support for the flash devices on the Altera - Excalibur XA10 Development Board. If you are building a kernel - for on of these boards then you should say 'Y' otherwise say 'N'. - config MTD_FORTUNET tristate "CFI Flash device mapped on the FortuNet board" depends on MTD_CFI && MTD_PARTITIONS && SA1100_FORTUNET diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 2f7e254912f..ab71f172eb7 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o -obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o diff --git a/drivers/mtd/maps/epxa10db-flash.c b/drivers/mtd/maps/epxa10db-flash.c deleted file mode 100644 index 265b079fe93..00000000000 --- a/drivers/mtd/maps/epxa10db-flash.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Flash memory access on EPXA based devices - * - * (C) 2000 Nicolas Pitre <nico@cam.org> - * Copyright (C) 2001 Altera Corporation - * Copyright (C) 2001 Red Hat, Inc. - * - * $Id: epxa10db-flash.c,v 1.15 2005/11/07 11:14:27 gleixner Exp $ - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/slab.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> -#include <asm/hardware.h> - -#ifdef CONFIG_EPXA10DB -#define BOARD_NAME "EPXA10DB" -#else -#define BOARD_NAME "EPXA1DB" -#endif - -static int nr_parts = 0; -static struct mtd_partition *parts; - -static struct mtd_info *mymtd; - -static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); - - -static struct map_info epxa_map = { - .name = "EPXA flash", - .size = FLASH_SIZE, - .bankwidth = 2, - .phys = FLASH_START, -}; - -static const char *probes[] = { "RedBoot", "afs", NULL }; - -static int __init epxa_mtd_init(void) -{ - int i; - - printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); - - epxa_map.virt = ioremap(FLASH_START, FLASH_SIZE); - if (!epxa_map.virt) { - printk("Failed to ioremap %s flash\n",BOARD_NAME); - return -EIO; - } - simple_map_init(&epxa_map); - - mymtd = do_map_probe("cfi_probe", &epxa_map); - if (!mymtd) { - iounmap((void *)epxa_map.virt); - return -ENXIO; - } - - mymtd->owner = THIS_MODULE; - - /* Unlock the flash device. */ - if(mymtd->unlock){ - for (i=0; i<mymtd->numeraseregions;i++){ - int j; - for(j=0;j<mymtd->eraseregions[i].numblocks;j++){ - mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); - } - } - } - -#ifdef CONFIG_MTD_PARTITIONS - nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0); - - if (nr_parts > 0) { - add_mtd_partitions(mymtd, parts, nr_parts); - return 0; - } -#endif - /* No recognised partitioning schemes found - use defaults */ - nr_parts = epxa_default_partitions(mymtd, &parts); - if (nr_parts > 0) { - add_mtd_partitions(mymtd, parts, nr_parts); - return 0; - } - - /* If all else fails... */ - add_mtd_device(mymtd); - return 0; -} - -static void __exit epxa_mtd_cleanup(void) -{ - if (mymtd) { - if (nr_parts) - del_mtd_partitions(mymtd); - else - del_mtd_device(mymtd); - map_destroy(mymtd); - } - if (epxa_map.virt) { - iounmap((void *)epxa_map.virt); - epxa_map.virt = 0; - } -} - - -/* - * This will do for now, once we decide which bootldr we're finally - * going to use then we'll remove this function and do it properly - * - * Partions are currently (as offsets from base of flash): - * 0x00000000 - 0x003FFFFF - bootloader (!) - * 0x00400000 - 0x00FFFFFF - Flashdisk - */ - -static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) -{ - struct mtd_partition *parts; - int ret, i; - int npartitions = 0; - char *names; - const char *name = "jffs"; - - printk("Using default partitions for %s\n",BOARD_NAME); - npartitions=1; - parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); - memzero(parts,npartitions*sizeof(*parts)+strlen(name)); - if (!parts) { - ret = -ENOMEM; - goto out; - } - i=0; - names = (char *)&parts[npartitions]; - parts[i].name = names; - names += strlen(name) + 1; - strcpy(parts[i].name, name); - -#ifdef CONFIG_EPXA10DB - parts[i].size = FLASH_SIZE-0x00400000; - parts[i].offset = 0x00400000; -#else - parts[i].size = FLASH_SIZE-0x00180000; - parts[i].offset = 0x00180000; -#endif - - out: - *pparts = parts; - return npartitions; -} - - -module_init(epxa_mtd_init); -module_exit(epxa_mtd_cleanup); - -MODULE_AUTHOR("Clive Davies"); -MODULE_DESCRIPTION("Altera epxa mtd flash map"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index af24216a062..f0f8916da7a 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -66,9 +66,6 @@ struct pcmciamtd_dev { }; -static dev_info_t dev_info = "pcmciamtd"; -static dev_link_t *dev_list; - /* Module parameters */ /* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ @@ -691,55 +688,21 @@ static void pcmciamtd_config(dev_link_t *link) } -/* The card status event handler. Mostly, this schedules other - * stuff to run after an event is received. A CARD_REMOVAL event - * also sets some flags to discourage the driver from trying - * to talk to the card any more. - */ +static int pcmciamtd_suspend(struct pcmcia_device *dev) +{ + DEBUG(2, "EVENT_PM_RESUME"); + + /* get_lock(link); */ + + return 0; +} -static int pcmciamtd_event(event_t event, int priority, - event_callback_args_t *args) +static int pcmciamtd_resume(struct pcmcia_device *dev) { - dev_link_t *link = args->client_data; - - DEBUG(1, "event=0x%06x", event); - switch (event) { - case CS_EVENT_CARD_REMOVAL: - DEBUG(2, "EVENT_CARD_REMOVAL"); - link->state &= ~DEV_PRESENT; - if (link->state & DEV_CONFIG) { - struct pcmciamtd_dev *dev = link->priv; - if(dev->mtd_info) { - del_mtd_device(dev->mtd_info); - info("mtd%d: Removed", dev->mtd_info->index); - } - pcmciamtd_release(link); - } - break; - case CS_EVENT_CARD_INSERTION: - DEBUG(2, "EVENT_CARD_INSERTION"); - link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; - pcmciamtd_config(link); - break; - case CS_EVENT_PM_SUSPEND: - DEBUG(2, "EVENT_PM_SUSPEND"); - link->state |= DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_RESET_PHYSICAL: - DEBUG(2, "EVENT_RESET_PHYSICAL"); - /* get_lock(link); */ - break; - case CS_EVENT_PM_RESUME: - DEBUG(2, "EVENT_PM_RESUME"); - link->state &= ~DEV_SUSPEND; - /* Fall through... */ - case CS_EVENT_CARD_RESET: - DEBUG(2, "EVENT_CARD_RESET"); - /* free_lock(link); */ - break; - default: - DEBUG(2, "Unknown event %d", event); - } + DEBUG(2, "EVENT_PM_SUSPEND"); + + /* free_lock(link); */ + return 0; } @@ -750,23 +713,21 @@ static int pcmciamtd_event(event_t event, int priority, * when the device is released. */ -static void pcmciamtd_detach(dev_link_t *link) +static void pcmciamtd_detach(struct pcmcia_device *p_dev) { + dev_link_t *link = dev_to_instance(p_dev); + DEBUG(3, "link=0x%p", link); if(link->state & DEV_CONFIG) { - pcmciamtd_release(link); - } + struct pcmciamtd_dev *dev = link->priv; + if(dev->mtd_info) { + del_mtd_device(dev->mtd_info); + info("mtd%d: Removed", dev->mtd_info->index); + } - if (link->handle) { - int ret; - DEBUG(2, "Deregistering with card services"); - ret = pcmcia_deregister_client(link->handle); - if (ret != CS_SUCCESS) - cs_error(link->handle, DeregisterClient, ret); + pcmciamtd_release(link); } - - link->state |= DEV_STALE_LINK; } @@ -775,16 +736,14 @@ static void pcmciamtd_detach(dev_link_t *link) * with Card Services. */ -static dev_link_t *pcmciamtd_attach(void) +static int pcmciamtd_attach(struct pcmcia_device *p_dev) { struct pcmciamtd_dev *dev; dev_link_t *link; - client_reg_t client_reg; - int ret; /* Create new memory card device */ dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) return NULL; + if (!dev) return -ENOMEM; DEBUG(1, "dev=0x%p", dev); memset(dev, 0, sizeof(*dev)); @@ -794,22 +753,14 @@ static dev_link_t *pcmciamtd_attach(void) link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY; - link->next = dev_list; - dev_list = link; - - /* Register with Card Services */ - client_reg.dev_info = &dev_info; - client_reg.Version = 0x0210; - client_reg.event_callback_args.client_data = link; - DEBUG(2, "Calling RegisterClient"); - ret = pcmcia_register_client(&link->handle, &client_reg); - if (ret != 0) { - cs_error(link->handle, RegisterClient, ret); - pcmciamtd_detach(link); - return NULL; - } - DEBUG(2, "link = %p", link); - return link; + link->next = NULL; + link->handle = p_dev; + p_dev->instance = link; + + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + pcmciamtd_config(link); + + return 0; } static struct pcmcia_device_id pcmciamtd_ids[] = { @@ -843,11 +794,12 @@ static struct pcmcia_driver pcmciamtd_driver = { .drv = { .name = "pcmciamtd" }, - .attach = pcmciamtd_attach, - .event = pcmciamtd_event, - .detach = pcmciamtd_detach, + .probe = pcmciamtd_attach, + .remove = pcmciamtd_detach, .owner = THIS_MODULE, .id_table = pcmciamtd_ids, + .suspend = pcmciamtd_suspend, + .resume = pcmciamtd_resume, }; @@ -875,7 +827,6 @@ static void __exit exit_pcmciamtd(void) { DEBUG(1, DRIVER_DESC " unloading"); pcmcia_unregister_driver(&pcmciamtd_driver); - BUG_ON(dev_list != NULL); } module_init(init_pcmciamtd); diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 339cb1218ea..7f3ff500b68 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -194,6 +194,14 @@ static int blktrans_release(struct inode *i, struct file *f) return ret; } +static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; + + if (dev->tr->getgeo) + return dev->tr->getgeo(dev, geo); + return -ENOTTY; +} static int blktrans_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) @@ -207,22 +215,6 @@ static int blktrans_ioctl(struct inode *inode, struct file *file, return tr->flush(dev); /* The core code did the work, we had nothing to do. */ return 0; - - case HDIO_GETGEO: - if (tr->getgeo) { - struct hd_geometry g; - int ret; - - memset(&g, 0, sizeof(g)); - ret = tr->getgeo(dev, &g); - if (ret) - return ret; - - g.start = get_start_sect(inode->i_bdev); - if (copy_to_user((void __user *)arg, &g, sizeof(g))) - return -EFAULT; - return 0; - } /* else */ default: return -ENOTTY; } @@ -233,6 +225,7 @@ struct block_device_operations mtd_blktrans_ops = { .open = blktrans_open, .release = blktrans_release, .ioctl = blktrans_ioctl, + .getgeo = blktrans_getgeo, }; int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 9c5945d6df8..201e1362da1 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -43,7 +43,7 @@ static int nand_width = 1; /* default x8*/ /* * Define partitions for flash device */ -const static struct mtd_partition partition_info[] = { +static const struct mtd_partition partition_info[] = { { .name = "NAND FS 0", .offset = 0, diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 21d4e8f4b7a..ec5e45e4e4e 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1506,7 +1506,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd) return 1; } -static inline int __init doc_probe(unsigned long physadr) +static int __init doc_probe(unsigned long physadr) { unsigned char ChipID; struct mtd_info *mtd; diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 3a5841c9d95..4129c03dfd9 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -96,7 +96,7 @@ static struct mtd_info *rtc_from4_mtd = NULL; */ static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); -const static struct mtd_partition partition_info[] = { +static const struct mtd_partition partition_info[] = { { .name = "Renesas flash partition 1", .offset = 0, diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d209214b131..5b55599739f 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -53,6 +53,7 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/clk.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -60,7 +61,6 @@ #include <linux/mtd/partitions.h> #include <asm/io.h> -#include <asm/hardware/clock.h> #include <asm/arch/regs-nand.h> #include <asm/arch/nand.h> @@ -460,7 +460,6 @@ static int s3c2410_nand_remove(struct platform_device *pdev) if (info->clk != NULL && !IS_ERR(info->clk)) { clk_disable(info->clk); - clk_unuse(info->clk); clk_put(info->clk); } @@ -598,7 +597,6 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440) goto exit_error; } - clk_use(info->clk); clk_enable(info->clk); /* allocate and map the resource */ diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 32541cbb010..9cf1ce718ec 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -67,7 +67,7 @@ module_param(spia_peddr, int, 0); /* * Define partitions for flash device */ -const static struct mtd_partition partition_info[] = { +static const struct mtd_partition partition_info[] = { { .name = "SPIA flash partition 1", .offset = 0, diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 48cce431f89..af06a80f44d 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -12,9 +12,10 @@ * This is a device driver for the OneNAND flash for generic boards. */ -#include <linux/device.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/slab.h> +#include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> #include <linux/mtd/partitions.h> @@ -39,7 +40,7 @@ static int __devinit generic_onenand_probe(struct device *dev) { struct onenand_info *info; struct platform_device *pdev = to_platform_device(dev); - struct onenand_platform_data *pdata = pdev->dev.platform_data; + struct flash_platform_data *pdata = pdev->dev.platform_data; struct resource *res = pdev->resource; unsigned long size = res->end - res->start + 1; int err; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index f67d5d6eb9a..a53a73fc2a5 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -940,7 +940,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, u_char *eccbuf, struct nand_oobinfo *oobsel) { struct onenand_chip *this = mtd->priv; - unsigned char buffer[MAX_ONENAND_PAGESIZE], *pbuf; + unsigned char *pbuf; size_t total_len, len; int i, written = 0; int ret = 0; @@ -975,7 +975,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, /* Loop until all keve's data has been written */ len = 0; while (count) { - pbuf = buffer; + pbuf = this->page_buf; /* * If the given tuple is >= pagesize then * write it out from the iov @@ -995,7 +995,7 @@ static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, int cnt = 0, thislen; while (cnt < mtd->oobblock) { thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len); - memcpy(buffer + cnt, vecs->iov_base + len, thislen); + memcpy(this->page_buf + cnt, vecs->iov_base + len, thislen); cnt += thislen; len += thislen; @@ -1296,6 +1296,12 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Block lock scheme */ for (block = start; block < end; block++) { + /* Set block address */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ @@ -1309,10 +1315,6 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) & ONENAND_CTRL_ONGO) continue; - /* Set block address for read block status */ - value = onenand_block_address(this, block); - this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); - /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & ONENAND_WP_US)) @@ -1346,7 +1348,6 @@ static void onenand_print_device_info(int device) static const struct onenand_manufacturers onenand_manuf_ids[] = { {ONENAND_MFR_SAMSUNG, "Samsung"}, - {ONENAND_MFR_UNKNOWN, "Unknown"} }; /** @@ -1357,17 +1358,22 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = { */ static int onenand_check_maf(int manuf) { + int size = ARRAY_SIZE(onenand_manuf_ids); + char *name; int i; - for (i = 0; onenand_manuf_ids[i].id; i++) { + for (i = 0; i < size; i++) if (manuf == onenand_manuf_ids[i].id) break; - } - printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", - onenand_manuf_ids[i].name, manuf); + if (i < size) + name = onenand_manuf_ids[i].name; + else + name = "Unknown"; + + printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); - return (i != ONENAND_MFR_UNKNOWN); + return (i == size); } /** @@ -1513,6 +1519,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) this->read_bufferram = onenand_sync_read_bufferram; } + /* Allocate buffers, if necessary */ + if (!this->page_buf) { + size_t len; + len = mtd->oobblock + mtd->oobsize; + this->page_buf = kmalloc(len, GFP_KERNEL); + if (!this->page_buf) { + printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); + return -ENOMEM; + } + this->options |= ONENAND_PAGEBUF_ALLOC; + } + this->state = FL_READY; init_waitqueue_head(&this->wq); spin_lock_init(&this->chip_lock); @@ -1574,12 +1592,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) */ void onenand_release(struct mtd_info *mtd) { + struct onenand_chip *this = mtd->priv; + #ifdef CONFIG_MTD_PARTITIONS /* Deregister partitions */ del_mtd_partitions (mtd); #endif /* Deregister the device */ del_mtd_device (mtd); + + /* Free bad block table memory, if allocated */ + if (this->bbm) + kfree(this->bbm); + /* Buffer allocated by onenand_scan */ + if (this->options & ONENAND_PAGEBUF_ALLOC) + kfree(this->page_buf); } EXPORT_SYMBOL_GPL(onenand_scan); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index f40190f499e..4510d3361ea 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -118,10 +118,10 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr */ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) { - unsigned char data_buf[MAX_ONENAND_PAGESIZE]; + struct onenand_chip *this = mtd->priv; bd->options &= ~NAND_BBT_SCANEMPTY; - return create_bbt(mtd, data_buf, bd, -1); + return create_bbt(mtd, this->page_buf, bd, -1); } /** diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 20ce212638f..a3e00a4635a 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -18,6 +18,7 @@ #include <linux/mtd/blktrans.h> #include <linux/mtd/mtd.h> #include <linux/vmalloc.h> +#include <linux/slab.h> #include <linux/jiffies.h> #include <asm/types.h> |