mirror of https://gitee.com/openkylin/linux.git
- Lots of cleanups from Artem, including deletion of some obsolete drivers
- Support partitions larger than 4GiB in device tree - Support for new SPI chips -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iEYEABECAAYFAlGLxzEACgkQdwG7hYl686M+PgCdHAn3fDzGW7gUL1tj43NCqaC8 PWoAoNAD5YpI3wYEBxped2MjSfgbQMvq =hM2T -----END PGP SIGNATURE----- Merge tag 'for-linus-20130509' of git://git.infradead.org/linux-mtd Pull MTD update from David Woodhouse: - Lots of cleanups from Artem, including deletion of some obsolete drivers - Support partitions larger than 4GiB in device tree - Support for new SPI chips * tag 'for-linus-20130509' of git://git.infradead.org/linux-mtd: (83 commits) mtd: omap2: Use module_platform_driver() mtd: bf5xx_nand: Use module_platform_driver() mtd: denali_dt: Remove redundant use of of_match_ptr mtd: denali_dt: Change return value to fix smatch warning mtd: denali_dt: Use module_platform_driver() mtd: denali_dt: Fix incorrect error check mtd: nand: subpage write support for hardware based ECC schemes mtd: omap2: use msecs_to_jiffies() mtd: nand_ids: use size macros mtd: nand_ids: improve LEGACY_ID_NAND macro a bit mtd: add 4 Toshiba nand chips for the full-id case mtd: add the support to parse out the full-id nand type mtd: add new fields to nand_flash_dev{} mtd: sh_flctl: Use of_match_ptr() macro mtd: gpio: Use of_match_ptr() macro mtd: gpio: Use devm_kzalloc() mtd: davinci_nand: Use of_match_ptr() mtd: dataflash: Use of_match_ptr() macro mtd: remove h720x flash support mtd: onenand: remove OneNAND simulator ...
This commit is contained in:
commit
a637b0d459
|
@ -14,8 +14,7 @@ Description:
|
|||
The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond
|
||||
to each /dev/mtdX character device. These may represent
|
||||
physical/simulated flash devices, partitions on a flash
|
||||
device, or concatenated flash devices. They exist regardless
|
||||
of whether CONFIG_MTD_CHAR is actually enabled.
|
||||
device, or concatenated flash devices.
|
||||
|
||||
What: /sys/class/mtd/mtdXro/
|
||||
Date: April 2009
|
||||
|
@ -23,8 +22,7 @@ KernelVersion: 2.6.29
|
|||
Contact: linux-mtd@lists.infradead.org
|
||||
Description:
|
||||
These directories provide the corresponding read-only device
|
||||
nodes for /sys/class/mtd/mtdX/ . They are only created
|
||||
(for the benefit of udev) if CONFIG_MTD_CHAR is enabled.
|
||||
nodes for /sys/class/mtd/mtdX/ .
|
||||
|
||||
What: /sys/class/mtd/mtdX/dev
|
||||
Date: April 2009
|
||||
|
|
|
@ -5,8 +5,12 @@ on platforms which have strong conventions about which portions of a flash are
|
|||
used for what purposes, but which don't use an on-flash partition table such
|
||||
as RedBoot.
|
||||
|
||||
#address-cells & #size-cells must both be present in the mtd device and be
|
||||
equal to 1.
|
||||
#address-cells & #size-cells must both be present in the mtd device. There are
|
||||
two valid values for both:
|
||||
<1>: for partitions that require a single 32-bit cell to represent their
|
||||
size/address (aka the value is below 4 GiB)
|
||||
<2>: for partitions that require two 32-bit cells to represent their
|
||||
size/address (aka the value is 4 GiB or greater).
|
||||
|
||||
Required properties:
|
||||
- reg : The partition's offset and size within the mtd bank.
|
||||
|
@ -36,3 +40,31 @@ flash@0 {
|
|||
reg = <0x0100000 0x200000>;
|
||||
};
|
||||
};
|
||||
|
||||
flash@1 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <2>;
|
||||
|
||||
/* a 4 GiB partition */
|
||||
partition@0 {
|
||||
label = "filesystem";
|
||||
reg = <0x00000000 0x1 0x00000000>;
|
||||
};
|
||||
};
|
||||
|
||||
flash@2 {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
/* an 8 GiB partition */
|
||||
partition@0 {
|
||||
label = "filesystem #1";
|
||||
reg = <0x0 0x00000000 0x2 0x00000000>;
|
||||
};
|
||||
|
||||
/* a 4 GiB partition */
|
||||
partition@200000000 {
|
||||
label = "filesystem #2";
|
||||
reg = <0x2 0x00000000 0x1 0x00000000>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -162,7 +162,6 @@ config MACH_XCEP
|
|||
select MTD
|
||||
select MTD_CFI
|
||||
select MTD_CFI_INTELEXT
|
||||
select MTD_CHAR
|
||||
select MTD_PHYSMAP
|
||||
select PXA25x
|
||||
select SMC91X
|
||||
|
|
|
@ -264,7 +264,6 @@ config ETRAX_AXISFLASHMAP
|
|||
select MTD_CFI
|
||||
select MTD_CFI_AMDSTD
|
||||
select MTD_JEDECPROBE if ETRAX_ARCH_V32
|
||||
select MTD_CHAR
|
||||
select MTD_BLOCK
|
||||
select MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
|
|
|
@ -404,7 +404,6 @@ config ETRAX_AXISFLASHMAP
|
|||
select MTD_CFI
|
||||
select MTD_CFI_AMDSTD
|
||||
select MTD_JEDECPROBE
|
||||
select MTD_CHAR
|
||||
select MTD_BLOCK
|
||||
select MTD_COMPLEX_MAPPINGS
|
||||
help
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <linux/serial_reg.h>
|
||||
#include <linux/time.h>
|
||||
|
||||
static const char *part_probes[] = { "bcm47xxpart", NULL };
|
||||
static const char * const part_probes[] = { "bcm47xxpart", NULL };
|
||||
|
||||
static struct physmap_flash_data bcma_pflash_data = {
|
||||
.part_probe_types = part_probes,
|
||||
|
|
|
@ -157,19 +157,6 @@ config MTD_BCM47XX_PARTS
|
|||
|
||||
comment "User Modules And Translation Layers"
|
||||
|
||||
config MTD_CHAR
|
||||
tristate "Direct char device access to MTD devices"
|
||||
help
|
||||
This provides a character device for each MTD device present in
|
||||
the system, allowing the user to read and write directly to the
|
||||
memory chips, and also use ioctl() to obtain information about
|
||||
the device, or to erase parts of it.
|
||||
|
||||
config HAVE_MTD_OTP
|
||||
bool
|
||||
help
|
||||
Enable access to OTP regions using MTD_CHAR.
|
||||
|
||||
config MTD_BLKDEVS
|
||||
tristate "Common interface to block layer for MTD 'translation layers'"
|
||||
depends on BLOCK
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
# Core functionality.
|
||||
obj-$(CONFIG_MTD) += mtd.o
|
||||
mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o
|
||||
mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o
|
||||
|
||||
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
|
||||
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
|
||||
|
@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
|
|||
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
|
||||
|
||||
# 'Users' - code which presents functionality to userspace.
|
||||
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
|
||||
obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
|
||||
obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
|
||||
obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o
|
||||
|
|
|
@ -146,7 +146,6 @@ config MTD_CFI_I8
|
|||
config MTD_OTP
|
||||
bool "Protection Registers aka one-time programmable (OTP) bits"
|
||||
depends on MTD_CFI_ADV_OPTIONS
|
||||
select HAVE_MTD_OTP
|
||||
default n
|
||||
help
|
||||
This enables support for reading, writing and locking so called
|
||||
|
|
|
@ -71,7 +71,6 @@ config MTD_DATAFLASH_WRITE_VERIFY
|
|||
config MTD_DATAFLASH_OTP
|
||||
bool "DataFlash OTP support (Security Register)"
|
||||
depends on MTD_DATAFLASH
|
||||
select HAVE_MTD_OTP
|
||||
help
|
||||
Newer DataFlash chips (revisions C and D) support 128 bytes of
|
||||
one-time-programmable (OTP) data. The first half may be written
|
||||
|
@ -205,69 +204,6 @@ config MTD_BLOCK2MTD
|
|||
|
||||
comment "Disk-On-Chip Device Drivers"
|
||||
|
||||
config MTD_DOC2000
|
||||
tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)"
|
||||
depends on MTD_NAND
|
||||
select MTD_DOCPROBE
|
||||
select MTD_NAND_IDS
|
||||
---help---
|
||||
This provides an MTD device driver for the M-Systems DiskOnChip
|
||||
2000 and Millennium devices. Originally designed for the DiskOnChip
|
||||
2000, it also now includes support for the DiskOnChip Millennium.
|
||||
If you have problems with this driver and the DiskOnChip Millennium,
|
||||
you may wish to try the alternative Millennium driver below. To use
|
||||
the alternative driver, you will need to undefine DOC_SINGLE_DRIVER
|
||||
in the <file:drivers/mtd/devices/docprobe.c> source code.
|
||||
|
||||
If you use this device, you probably also want to enable the NFTL
|
||||
'NAND Flash Translation Layer' option below, which is used to
|
||||
emulate a block device by using a kind of file system on the flash
|
||||
chips.
|
||||
|
||||
NOTE: This driver is deprecated and will probably be removed soon.
|
||||
Please try the new DiskOnChip driver under "NAND Flash Device
|
||||
Drivers".
|
||||
|
||||
config MTD_DOC2001
|
||||
tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)"
|
||||
depends on MTD_NAND
|
||||
select MTD_DOCPROBE
|
||||
select MTD_NAND_IDS
|
||||
---help---
|
||||
This provides an alternative MTD device driver for the M-Systems
|
||||
DiskOnChip Millennium devices. Use this if you have problems with
|
||||
the combined DiskOnChip 2000 and Millennium driver above. To get
|
||||
the DiskOnChip probe code to load and use this driver instead of
|
||||
the other one, you will need to undefine DOC_SINGLE_DRIVER near
|
||||
the beginning of <file:drivers/mtd/devices/docprobe.c>.
|
||||
|
||||
If you use this device, you probably also want to enable the NFTL
|
||||
'NAND Flash Translation Layer' option below, which is used to
|
||||
emulate a block device by using a kind of file system on the flash
|
||||
chips.
|
||||
|
||||
NOTE: This driver is deprecated and will probably be removed soon.
|
||||
Please try the new DiskOnChip driver under "NAND Flash Device
|
||||
Drivers".
|
||||
|
||||
config MTD_DOC2001PLUS
|
||||
tristate "M-Systems Disk-On-Chip Millennium Plus"
|
||||
depends on MTD_NAND
|
||||
select MTD_DOCPROBE
|
||||
select MTD_NAND_IDS
|
||||
---help---
|
||||
This provides an MTD device driver for the M-Systems DiskOnChip
|
||||
Millennium Plus devices.
|
||||
|
||||
If you use this device, you probably also want to enable the INFTL
|
||||
'Inverse NAND Flash Translation Layer' option below, which is used
|
||||
to emulate a block device by using a kind of file system on the
|
||||
flash chips.
|
||||
|
||||
NOTE: This driver will soon be replaced by the new DiskOnChip driver
|
||||
under "NAND Flash Device Drivers" (currently that driver does not
|
||||
support all Millennium Plus devices).
|
||||
|
||||
config MTD_DOCG3
|
||||
tristate "M-Systems Disk-On-Chip G3"
|
||||
select BCH
|
||||
|
|
|
@ -2,12 +2,7 @@
|
|||
# linux/drivers/mtd/devices/Makefile
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
|
||||
obj-$(CONFIG_MTD_DOC2001) += doc2001.o
|
||||
obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
|
||||
obj-$(CONFIG_MTD_DOCG3) += docg3.o
|
||||
obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o
|
||||
obj-$(CONFIG_MTD_DOCECC) += docecc.o
|
||||
obj-$(CONFIG_MTD_SLRAM) += slram.o
|
||||
obj-$(CONFIG_MTD_PHRAM) += phram.o
|
||||
obj-$(CONFIG_MTD_PMC551) += pmc551.o
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Serial flash driver for BCMA bus");
|
||||
|
||||
static const char *probes[] = { "bcm47xxpart", NULL };
|
||||
static const char * const probes[] = { "bcm47xxpart", NULL };
|
||||
|
||||
static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
|
@ -61,6 +61,17 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
|
|||
}
|
||||
sflash->priv = b47s;
|
||||
|
||||
b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash);
|
||||
|
||||
switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) {
|
||||
case BCMA_CC_FLASHT_STSER:
|
||||
b47s->type = BCM47XXSFLASH_TYPE_ST;
|
||||
break;
|
||||
case BCMA_CC_FLASHT_ATSER:
|
||||
b47s->type = BCM47XXSFLASH_TYPE_ATMEL;
|
||||
break;
|
||||
}
|
||||
|
||||
b47s->window = sflash->window;
|
||||
b47s->blocksize = sflash->blocksize;
|
||||
b47s->numblocks = sflash->numblocks;
|
||||
|
|
|
@ -3,7 +3,66 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
/* Used for ST flashes only. */
|
||||
#define OPCODE_ST_WREN 0x0006 /* Write Enable */
|
||||
#define OPCODE_ST_WRDIS 0x0004 /* Write Disable */
|
||||
#define OPCODE_ST_RDSR 0x0105 /* Read Status Register */
|
||||
#define OPCODE_ST_WRSR 0x0101 /* Write Status Register */
|
||||
#define OPCODE_ST_READ 0x0303 /* Read Data Bytes */
|
||||
#define OPCODE_ST_PP 0x0302 /* Page Program */
|
||||
#define OPCODE_ST_SE 0x02d8 /* Sector Erase */
|
||||
#define OPCODE_ST_BE 0x00c7 /* Bulk Erase */
|
||||
#define OPCODE_ST_DP 0x00b9 /* Deep Power-down */
|
||||
#define OPCODE_ST_RES 0x03ab /* Read Electronic Signature */
|
||||
#define OPCODE_ST_CSA 0x1000 /* Keep chip select asserted */
|
||||
#define OPCODE_ST_SSE 0x0220 /* Sub-sector Erase */
|
||||
|
||||
/* Used for Atmel flashes only. */
|
||||
#define OPCODE_AT_READ 0x07e8
|
||||
#define OPCODE_AT_PAGE_READ 0x07d2
|
||||
#define OPCODE_AT_STATUS 0x01d7
|
||||
#define OPCODE_AT_BUF1_WRITE 0x0384
|
||||
#define OPCODE_AT_BUF2_WRITE 0x0387
|
||||
#define OPCODE_AT_BUF1_ERASE_PROGRAM 0x0283
|
||||
#define OPCODE_AT_BUF2_ERASE_PROGRAM 0x0286
|
||||
#define OPCODE_AT_BUF1_PROGRAM 0x0288
|
||||
#define OPCODE_AT_BUF2_PROGRAM 0x0289
|
||||
#define OPCODE_AT_PAGE_ERASE 0x0281
|
||||
#define OPCODE_AT_BLOCK_ERASE 0x0250
|
||||
#define OPCODE_AT_BUF1_WRITE_ERASE_PROGRAM 0x0382
|
||||
#define OPCODE_AT_BUF2_WRITE_ERASE_PROGRAM 0x0385
|
||||
#define OPCODE_AT_BUF1_LOAD 0x0253
|
||||
#define OPCODE_AT_BUF2_LOAD 0x0255
|
||||
#define OPCODE_AT_BUF1_COMPARE 0x0260
|
||||
#define OPCODE_AT_BUF2_COMPARE 0x0261
|
||||
#define OPCODE_AT_BUF1_REPROGRAM 0x0258
|
||||
#define OPCODE_AT_BUF2_REPROGRAM 0x0259
|
||||
|
||||
/* Status register bits for ST flashes */
|
||||
#define SR_ST_WIP 0x01 /* Write In Progress */
|
||||
#define SR_ST_WEL 0x02 /* Write Enable Latch */
|
||||
#define SR_ST_BP_MASK 0x1c /* Block Protect */
|
||||
#define SR_ST_BP_SHIFT 2
|
||||
#define SR_ST_SRWD 0x80 /* Status Register Write Disable */
|
||||
|
||||
/* Status register bits for Atmel flashes */
|
||||
#define SR_AT_READY 0x80
|
||||
#define SR_AT_MISMATCH 0x40
|
||||
#define SR_AT_ID_MASK 0x38
|
||||
#define SR_AT_ID_SHIFT 3
|
||||
|
||||
struct bcma_drv_cc;
|
||||
|
||||
enum bcm47xxsflash_type {
|
||||
BCM47XXSFLASH_TYPE_ATMEL,
|
||||
BCM47XXSFLASH_TYPE_ST,
|
||||
};
|
||||
|
||||
struct bcm47xxsflash {
|
||||
struct bcma_drv_cc *bcma_cc;
|
||||
|
||||
enum bcm47xxsflash_type type;
|
||||
|
||||
u32 window;
|
||||
u32 blocksize;
|
||||
u16 numblocks;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,824 +0,0 @@
|
|||
|
||||
/*
|
||||
* Linux driver for Disk-On-Chip Millennium
|
||||
* (c) 1999 Machine Vision Holdings, Inc.
|
||||
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/doc2000.h>
|
||||
|
||||
/* #define ECC_DEBUG */
|
||||
|
||||
/* I have no idea why some DoC chips can not use memcop_form|to_io().
|
||||
* This may be due to the different revisions of the ASIC controller built-in or
|
||||
* simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
|
||||
* this:*/
|
||||
#undef USE_MEMCPY
|
||||
|
||||
static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
|
||||
|
||||
static struct mtd_info *docmillist = NULL;
|
||||
|
||||
/* Perform the required delay cycles by reading from the NOP register */
|
||||
static void DoC_Delay(void __iomem * docptr, unsigned short cycles)
|
||||
{
|
||||
volatile char dummy;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cycles; i++)
|
||||
dummy = ReadDOC(docptr, NOP);
|
||||
}
|
||||
|
||||
/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
|
||||
static int _DoC_WaitReady(void __iomem * docptr)
|
||||
{
|
||||
unsigned short c = 0xffff;
|
||||
|
||||
pr_debug("_DoC_WaitReady called for out-of-line wait\n");
|
||||
|
||||
/* Out-of-line routine to wait for chip response */
|
||||
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
|
||||
;
|
||||
|
||||
if (c == 0)
|
||||
pr_debug("_DoC_WaitReady timed out.\n");
|
||||
|
||||
return (c == 0);
|
||||
}
|
||||
|
||||
static inline int DoC_WaitReady(void __iomem * docptr)
|
||||
{
|
||||
/* This is inline, to optimise the common case, where it's ready instantly */
|
||||
int ret = 0;
|
||||
|
||||
/* 4 read form NOP register should be issued in prior to the read from CDSNControl
|
||||
see Software Requirement 11.4 item 2. */
|
||||
DoC_Delay(docptr, 4);
|
||||
|
||||
if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
|
||||
/* Call the out-of-line routine to wait */
|
||||
ret = _DoC_WaitReady(docptr);
|
||||
|
||||
/* issue 2 read from NOP register after reading from CDSNControl register
|
||||
see Software Requirement 11.4 item 2. */
|
||||
DoC_Delay(docptr, 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* DoC_Command: Send a flash command to the flash chip through the CDSN IO register
|
||||
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 void DoC_Command(void __iomem * docptr, unsigned char command,
|
||||
unsigned char xtraflags)
|
||||
{
|
||||
/* Assert the CLE (Command Latch Enable) line to the flash chip */
|
||||
WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
|
||||
DoC_Delay(docptr, 4);
|
||||
|
||||
/* Send the command */
|
||||
WriteDOC(command, docptr, Mil_CDSN_IO);
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
|
||||
/* Lower the CLE line */
|
||||
WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
|
||||
DoC_Delay(docptr, 4);
|
||||
}
|
||||
|
||||
/* DoC_Address: Set the current address for the flash chip through the CDSN IO register
|
||||
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_Address(void __iomem * docptr, int numbytes, unsigned long ofs,
|
||||
unsigned char xtraflags1, unsigned char xtraflags2)
|
||||
{
|
||||
/* Assert the ALE (Address Latch Enable) line to the flash chip */
|
||||
WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
|
||||
DoC_Delay(docptr, 4);
|
||||
|
||||
/* Send the address */
|
||||
switch (numbytes)
|
||||
{
|
||||
case 1:
|
||||
/* Send single byte, bits 0-7. */
|
||||
WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO);
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
break;
|
||||
case 2:
|
||||
/* Send bits 9-16 followed by 17-23 */
|
||||
WriteDOC((ofs >> 9) & 0xff, docptr, Mil_CDSN_IO);
|
||||
WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO);
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
break;
|
||||
case 3:
|
||||
/* Send 0-7, 9-16, then 17-23 */
|
||||
WriteDOC(ofs & 0xff, docptr, Mil_CDSN_IO);
|
||||
WriteDOC((ofs >> 9) & 0xff, docptr, Mil_CDSN_IO);
|
||||
WriteDOC((ofs >> 17) & 0xff, docptr, Mil_CDSN_IO);
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lower the ALE line */
|
||||
WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl);
|
||||
DoC_Delay(docptr, 4);
|
||||
}
|
||||
|
||||
/* DoC_SelectChip: Select a given flash chip within the current floor */
|
||||
static int DoC_SelectChip(void __iomem * docptr, int chip)
|
||||
{
|
||||
/* Select the individual flash chip requested */
|
||||
WriteDOC(chip, docptr, CDSNDeviceSelect);
|
||||
DoC_Delay(docptr, 4);
|
||||
|
||||
/* Wait for it to be ready */
|
||||
return DoC_WaitReady(docptr);
|
||||
}
|
||||
|
||||
/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
|
||||
static int DoC_SelectFloor(void __iomem * docptr, int floor)
|
||||
{
|
||||
/* Select the floor (bank) of chips required */
|
||||
WriteDOC(floor, docptr, FloorSelect);
|
||||
|
||||
/* Wait for the chip to be ready */
|
||||
return DoC_WaitReady(docptr);
|
||||
}
|
||||
|
||||
/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
|
||||
static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
|
||||
{
|
||||
int mfr, id, i, j;
|
||||
volatile char dummy;
|
||||
|
||||
/* Page in the required floor/chip
|
||||
FIXME: is this supported by Millennium ?? */
|
||||
DoC_SelectFloor(doc->virtadr, floor);
|
||||
DoC_SelectChip(doc->virtadr, chip);
|
||||
|
||||
/* Reset the chip, see Software Requirement 11.4 item 1. */
|
||||
DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP);
|
||||
DoC_WaitReady(doc->virtadr);
|
||||
|
||||
/* Read the NAND chip ID: 1. Send ReadID command */
|
||||
DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP);
|
||||
|
||||
/* Read the NAND chip ID: 2. Send address byte zero */
|
||||
DoC_Address(doc->virtadr, 1, 0x00, CDSN_CTRL_WP, 0x00);
|
||||
|
||||
/* Read the manufacturer and device id codes of the flash device through
|
||||
CDSN IO register see Software Requirement 11.4 item 5.*/
|
||||
dummy = ReadDOC(doc->virtadr, ReadPipeInit);
|
||||
DoC_Delay(doc->virtadr, 2);
|
||||
mfr = ReadDOC(doc->virtadr, Mil_CDSN_IO);
|
||||
|
||||
DoC_Delay(doc->virtadr, 2);
|
||||
id = ReadDOC(doc->virtadr, Mil_CDSN_IO);
|
||||
dummy = ReadDOC(doc->virtadr, LastDataRead);
|
||||
|
||||
/* No response - return failure */
|
||||
if (mfr == 0xff || mfr == 0)
|
||||
return 0;
|
||||
|
||||
/* FIXME: to deal with multi-flash on multi-Millennium case more carefully */
|
||||
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
||||
if ( id == nand_flash_ids[i].id) {
|
||||
/* Try to identify manufacturer */
|
||||
for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
|
||||
if (nand_manuf_ids[j].id == mfr)
|
||||
break;
|
||||
}
|
||||
printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
|
||||
"Chip ID: %2.2X (%s:%s)\n",
|
||||
mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name);
|
||||
doc->mfr = mfr;
|
||||
doc->id = id;
|
||||
doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nand_flash_ids[i].name == NULL)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
|
||||
static void DoC_ScanChips(struct DiskOnChip *this)
|
||||
{
|
||||
int floor, chip;
|
||||
int numchips[MAX_FLOORS_MIL];
|
||||
int ret;
|
||||
|
||||
this->numchips = 0;
|
||||
this->mfr = 0;
|
||||
this->id = 0;
|
||||
|
||||
/* For each floor, find the number of valid chips it contains */
|
||||
for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) {
|
||||
numchips[floor] = 0;
|
||||
for (chip = 0; chip < MAX_CHIPS_MIL && ret != 0; chip++) {
|
||||
ret = DoC_IdentChip(this, floor, chip);
|
||||
if (ret) {
|
||||
numchips[floor]++;
|
||||
this->numchips++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If there are none at all that we recognise, bail */
|
||||
if (!this->numchips) {
|
||||
printk("No flash chips recognised.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allocate an array to hold the information for each chip */
|
||||
this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
|
||||
if (!this->chips){
|
||||
printk("No memory for allocating chip info structures\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fill out the chip array with {floor, chipno} for each
|
||||
* detected chip in the device. */
|
||||
for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) {
|
||||
for (chip = 0 ; chip < numchips[floor] ; chip++) {
|
||||
this->chips[ret].floor = floor;
|
||||
this->chips[ret].chip = chip;
|
||||
this->chips[ret].curadr = 0;
|
||||
this->chips[ret].curmode = 0x50;
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate and print the total size of the device */
|
||||
this->totlen = this->numchips * (1 << this->chipshift);
|
||||
printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n",
|
||||
this->numchips ,this->totlen >> 20);
|
||||
}
|
||||
|
||||
static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
|
||||
{
|
||||
int tmp1, tmp2, retval;
|
||||
|
||||
if (doc1->physadr == doc2->physadr)
|
||||
return 1;
|
||||
|
||||
/* Use the alias resolution register which was set aside for this
|
||||
* purpose. If it's value is the same on both chips, they might
|
||||
* be the same chip, and we write to one and check for a change in
|
||||
* the other. It's unclear if this register is usuable in the
|
||||
* DoC 2000 (it's in the Millenium docs), but it seems to work. */
|
||||
tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
|
||||
tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
|
||||
if (tmp1 != tmp2)
|
||||
return 0;
|
||||
|
||||
WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution);
|
||||
tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
|
||||
if (tmp2 == (tmp1+1) % 0xff)
|
||||
retval = 1;
|
||||
else
|
||||
retval = 0;
|
||||
|
||||
/* Restore register contents. May not be necessary, but do it just to
|
||||
* be safe. */
|
||||
WriteDOC(tmp1, doc1->virtadr, AliasResolution);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* This routine is found from the docprobe code by symbol_get(),
|
||||
* which will bump the use count of this module. */
|
||||
void DoCMil_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
struct DiskOnChip *old = NULL;
|
||||
|
||||
/* We must avoid being called twice for the same device. */
|
||||
if (docmillist)
|
||||
old = docmillist->priv;
|
||||
|
||||
while (old) {
|
||||
if (DoCMil_is_alias(this, old)) {
|
||||
printk(KERN_NOTICE "Ignoring DiskOnChip Millennium at "
|
||||
"0x%lX - already configured\n", this->physadr);
|
||||
iounmap(this->virtadr);
|
||||
kfree(mtd);
|
||||
return;
|
||||
}
|
||||
if (old->nextdoc)
|
||||
old = old->nextdoc->priv;
|
||||
else
|
||||
old = NULL;
|
||||
}
|
||||
|
||||
mtd->name = "DiskOnChip Millennium";
|
||||
printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n",
|
||||
this->physadr);
|
||||
|
||||
mtd->type = MTD_NANDFLASH;
|
||||
mtd->flags = MTD_CAP_NANDFLASH;
|
||||
|
||||
/* FIXME: erase size is not always 8KiB */
|
||||
mtd->erasesize = 0x2000;
|
||||
mtd->writebufsize = mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->ecc_strength = 2;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->_erase = doc_erase;
|
||||
mtd->_read = doc_read;
|
||||
mtd->_write = doc_write;
|
||||
mtd->_read_oob = doc_read_oob;
|
||||
mtd->_write_oob = doc_write_oob;
|
||||
this->curfloor = -1;
|
||||
this->curchip = -1;
|
||||
|
||||
/* Ident all the chips present. */
|
||||
DoC_ScanChips(this);
|
||||
|
||||
if (!this->totlen) {
|
||||
kfree(mtd);
|
||||
iounmap(this->virtadr);
|
||||
} else {
|
||||
this->nextdoc = docmillist;
|
||||
docmillist = mtd;
|
||||
mtd->size = this->totlen;
|
||||
mtd_device_register(mtd, NULL, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(DoCMil_init);
|
||||
|
||||
static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
int i, ret;
|
||||
volatile char dummy;
|
||||
unsigned char syndrome[6], eccbuf[6];
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
|
||||
|
||||
/* Don't allow a single read to cross a 512-byte block boundary */
|
||||
if (from + len > ((from | 0x1ff) + 1))
|
||||
len = ((from | 0x1ff) + 1) - from;
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
DoC_SelectFloor(docptr, mychip->floor);
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
} else if (this->curchip != mychip->chip) {
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
}
|
||||
this->curfloor = mychip->floor;
|
||||
this->curchip = mychip->chip;
|
||||
|
||||
/* issue the Read0 or Read1 command depend on which half of the page
|
||||
we are accessing. Polling the Flash Ready bit after issue 3 bytes
|
||||
address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
|
||||
DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
|
||||
DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
|
||||
/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
|
||||
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
|
||||
WriteDOC (DOC_ECC_EN, docptr, ECCConf);
|
||||
|
||||
/* Read the data via the internal pipeline through CDSN IO register,
|
||||
see Pipelined Read Operations 11.3 */
|
||||
dummy = ReadDOC(docptr, ReadPipeInit);
|
||||
#ifndef USE_MEMCPY
|
||||
for (i = 0; i < len-1; i++) {
|
||||
/* N.B. you have to increase the source address in this way or the
|
||||
ECC logic will not work properly */
|
||||
buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
|
||||
}
|
||||
#else
|
||||
memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
|
||||
#endif
|
||||
buf[len - 1] = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
/* Let the caller know we completed it */
|
||||
*retlen = len;
|
||||
ret = 0;
|
||||
|
||||
/* Read the ECC data from Spare Data Area,
|
||||
see Reed-Solomon EDC/ECC 11.1 */
|
||||
dummy = ReadDOC(docptr, ReadPipeInit);
|
||||
#ifndef USE_MEMCPY
|
||||
for (i = 0; i < 5; i++) {
|
||||
/* N.B. you have to increase the source address in this way or the
|
||||
ECC logic will not work properly */
|
||||
eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
|
||||
}
|
||||
#else
|
||||
memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5);
|
||||
#endif
|
||||
eccbuf[5] = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
/* Flush the pipeline */
|
||||
dummy = ReadDOC(docptr, ECCConf);
|
||||
dummy = ReadDOC(docptr, ECCConf);
|
||||
|
||||
/* Check the ECC Status */
|
||||
if (ReadDOC(docptr, ECCConf) & 0x80) {
|
||||
int nb_errors;
|
||||
/* There was an ECC error */
|
||||
#ifdef ECC_DEBUG
|
||||
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
|
||||
#endif
|
||||
/* Read the ECC syndrome through the DiskOnChip ECC logic.
|
||||
These syndrome will be all ZERO when there is no error */
|
||||
for (i = 0; i < 6; i++) {
|
||||
syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i);
|
||||
}
|
||||
nb_errors = doc_decode_ecc(buf, syndrome);
|
||||
#ifdef ECC_DEBUG
|
||||
printk("ECC Errors corrected: %x\n", nb_errors);
|
||||
#endif
|
||||
if (nb_errors < 0) {
|
||||
/* We return error, but have actually done the read. Not that
|
||||
this can be told to user-space, via sys_read(), but at least
|
||||
MTD-aware stuff can know about it by checking *retlen */
|
||||
ret = -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef PSYCHO_DEBUG
|
||||
printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
|
||||
(long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
|
||||
eccbuf[4], eccbuf[5]);
|
||||
#endif
|
||||
|
||||
/* disable the ECC engine */
|
||||
WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
int i,ret = 0;
|
||||
char eccbuf[6];
|
||||
volatile char dummy;
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[to >> (this->chipshift)];
|
||||
|
||||
#if 0
|
||||
/* Don't allow a single write to cross a 512-byte block boundary */
|
||||
if (to + len > ( (to | 0x1ff) + 1))
|
||||
len = ((to | 0x1ff) + 1) - to;
|
||||
#else
|
||||
/* Don't allow writes which aren't exactly one block */
|
||||
if (to & 0x1ff || len != 0x200)
|
||||
return -EINVAL;
|
||||
#endif
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
DoC_SelectFloor(docptr, mychip->floor);
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
} else if (this->curchip != mychip->chip) {
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
}
|
||||
this->curfloor = mychip->floor;
|
||||
this->curchip = mychip->chip;
|
||||
|
||||
/* Reset the chip, see Software Requirement 11.4 item 1. */
|
||||
DoC_Command(docptr, NAND_CMD_RESET, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
/* Set device to main plane of flash */
|
||||
DoC_Command(docptr, NAND_CMD_READ0, 0x00);
|
||||
|
||||
/* issue the Serial Data In command to initial the Page Program process */
|
||||
DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
|
||||
DoC_Address(docptr, 3, to, 0x00, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
|
||||
/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
|
||||
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
|
||||
WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
|
||||
|
||||
/* Write the data via the internal pipeline through CDSN IO register,
|
||||
see Pipelined Write Operations 11.2 */
|
||||
#ifndef USE_MEMCPY
|
||||
for (i = 0; i < len; i++) {
|
||||
/* N.B. you have to increase the source address in this way or the
|
||||
ECC logic will not work properly */
|
||||
WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
|
||||
}
|
||||
#else
|
||||
memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
|
||||
#endif
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
|
||||
/* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic
|
||||
see Reed-Solomon EDC/ECC 11.1 */
|
||||
WriteDOC(0, docptr, NOP);
|
||||
WriteDOC(0, docptr, NOP);
|
||||
WriteDOC(0, docptr, NOP);
|
||||
|
||||
/* Read the ECC data through the DiskOnChip ECC logic */
|
||||
for (i = 0; i < 6; i++) {
|
||||
eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i);
|
||||
}
|
||||
|
||||
/* ignore the ECC engine */
|
||||
WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
|
||||
|
||||
#ifndef USE_MEMCPY
|
||||
/* Write the ECC data to flash */
|
||||
for (i = 0; i < 6; i++) {
|
||||
/* N.B. you have to increase the source address in this way or the
|
||||
ECC logic will not work properly */
|
||||
WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i);
|
||||
}
|
||||
#else
|
||||
memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6);
|
||||
#endif
|
||||
|
||||
/* write the block status BLOCK_USED (0x5555) at the end of ECC data
|
||||
FIXME: this is only a hack for programming the IPL area for LinuxBIOS
|
||||
and should be replace with proper codes in user space utilities */
|
||||
WriteDOC(0x55, docptr, Mil_CDSN_IO);
|
||||
WriteDOC(0x55, docptr, Mil_CDSN_IO + 1);
|
||||
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
|
||||
#ifdef PSYCHO_DEBUG
|
||||
printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
|
||||
(long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
|
||||
eccbuf[4], eccbuf[5]);
|
||||
#endif
|
||||
|
||||
/* Commit the Page Program command and wait for ready
|
||||
see Software Requirement 11.4 item 1.*/
|
||||
DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
|
||||
/* Read the status of the flash device through CDSN IO register
|
||||
see Software Requirement 11.4 item 5.*/
|
||||
DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
|
||||
dummy = ReadDOC(docptr, ReadPipeInit);
|
||||
DoC_Delay(docptr, 2);
|
||||
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
|
||||
printk("Error programming flash\n");
|
||||
/* Error in programming
|
||||
FIXME: implement Bad Block Replacement (in nftl.c ??) */
|
||||
ret = -EIO;
|
||||
}
|
||||
dummy = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
/* Let the caller know we completed it */
|
||||
*retlen = len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
#ifndef USE_MEMCPY
|
||||
int i;
|
||||
#endif
|
||||
volatile char dummy;
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
DoC_SelectFloor(docptr, mychip->floor);
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
} else if (this->curchip != mychip->chip) {
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
}
|
||||
this->curfloor = mychip->floor;
|
||||
this->curchip = mychip->chip;
|
||||
|
||||
/* disable the ECC engine */
|
||||
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
|
||||
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
|
||||
|
||||
/* issue the Read2 command to set the pointer to the Spare Data Area.
|
||||
Polling the Flash Ready bit after issue 3 bytes address in
|
||||
Sequence Read Mode, see Software Requirement 11.4 item 1.*/
|
||||
DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
|
||||
DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
|
||||
/* Read the data out via the internal pipeline through CDSN IO register,
|
||||
see Pipelined Read Operations 11.3 */
|
||||
dummy = ReadDOC(docptr, ReadPipeInit);
|
||||
#ifndef USE_MEMCPY
|
||||
for (i = 0; i < len-1; i++) {
|
||||
/* N.B. you have to increase the source address in this way or the
|
||||
ECC logic will not work properly */
|
||||
buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
|
||||
}
|
||||
#else
|
||||
memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
|
||||
#endif
|
||||
buf[len - 1] = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
ops->retlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
#ifndef USE_MEMCPY
|
||||
int i;
|
||||
#endif
|
||||
volatile char dummy;
|
||||
int ret = 0;
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OPS_PLACE_OOB);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
DoC_SelectFloor(docptr, mychip->floor);
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
} else if (this->curchip != mychip->chip) {
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
}
|
||||
this->curfloor = mychip->floor;
|
||||
this->curchip = mychip->chip;
|
||||
|
||||
/* disable the ECC engine */
|
||||
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
|
||||
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
|
||||
|
||||
/* Reset the chip, see Software Requirement 11.4 item 1. */
|
||||
DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
|
||||
DoC_WaitReady(docptr);
|
||||
/* issue the Read2 command to set the pointer to the Spare Data Area. */
|
||||
DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
|
||||
|
||||
/* issue the Serial Data In command to initial the Page Program process */
|
||||
DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
|
||||
DoC_Address(docptr, 3, ofs, 0x00, 0x00);
|
||||
|
||||
/* Write the data via the internal pipeline through CDSN IO register,
|
||||
see Pipelined Write Operations 11.2 */
|
||||
#ifndef USE_MEMCPY
|
||||
for (i = 0; i < len; i++) {
|
||||
/* N.B. you have to increase the source address in this way or the
|
||||
ECC logic will not work properly */
|
||||
WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
|
||||
}
|
||||
#else
|
||||
memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
|
||||
#endif
|
||||
WriteDOC(0x00, docptr, WritePipeTerm);
|
||||
|
||||
/* Commit the Page Program command and wait for ready
|
||||
see Software Requirement 11.4 item 1.*/
|
||||
DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
|
||||
/* Read the status of the flash device through CDSN IO register
|
||||
see Software Requirement 11.4 item 5.*/
|
||||
DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
|
||||
dummy = ReadDOC(docptr, ReadPipeInit);
|
||||
DoC_Delay(docptr, 2);
|
||||
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
|
||||
printk("Error programming oob data\n");
|
||||
/* FIXME: implement Bad Block Replacement (in nftl.c ??) */
|
||||
ops->retlen = 0;
|
||||
ret = -EIO;
|
||||
}
|
||||
dummy = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
ops->retlen = len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
volatile char dummy;
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
__u32 ofs = instr->addr;
|
||||
__u32 len = instr->len;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
|
||||
if (len != mtd->erasesize)
|
||||
printk(KERN_WARNING "Erase not right size (%x != %x)n",
|
||||
len, mtd->erasesize);
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
DoC_SelectFloor(docptr, mychip->floor);
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
} else if (this->curchip != mychip->chip) {
|
||||
DoC_SelectChip(docptr, mychip->chip);
|
||||
}
|
||||
this->curfloor = mychip->floor;
|
||||
this->curchip = mychip->chip;
|
||||
|
||||
instr->state = MTD_ERASE_PENDING;
|
||||
|
||||
/* issue the Erase Setup command */
|
||||
DoC_Command(docptr, NAND_CMD_ERASE1, 0x00);
|
||||
DoC_Address(docptr, 2, ofs, 0x00, 0x00);
|
||||
|
||||
/* Commit the Erase Start command and wait for ready
|
||||
see Software Requirement 11.4 item 1.*/
|
||||
DoC_Command(docptr, NAND_CMD_ERASE2, 0x00);
|
||||
DoC_WaitReady(docptr);
|
||||
|
||||
instr->state = MTD_ERASING;
|
||||
|
||||
/* Read the status of the flash device through CDSN IO register
|
||||
see Software Requirement 11.4 item 5.
|
||||
FIXME: it seems that we are not wait long enough, some blocks are not
|
||||
erased fully */
|
||||
DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
|
||||
dummy = ReadDOC(docptr, ReadPipeInit);
|
||||
DoC_Delay(docptr, 2);
|
||||
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
|
||||
printk("Error Erasing at 0x%x\n", ofs);
|
||||
/* There was an error
|
||||
FIXME: implement Bad Block Replacement (in nftl.c ??) */
|
||||
instr->state = MTD_ERASE_FAILED;
|
||||
} else
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
dummy = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Module stuff
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void __exit cleanup_doc2001(void)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
struct DiskOnChip *this;
|
||||
|
||||
while ((mtd=docmillist)) {
|
||||
this = mtd->priv;
|
||||
docmillist = this->nextdoc;
|
||||
|
||||
mtd_device_unregister(mtd);
|
||||
|
||||
iounmap(this->virtadr);
|
||||
kfree(this->chips);
|
||||
kfree(mtd);
|
||||
}
|
||||
}
|
||||
|
||||
module_exit(cleanup_doc2001);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
|
||||
MODULE_DESCRIPTION("Alternative driver for DiskOnChip Millennium");
|
File diff suppressed because it is too large
Load Diff
|
@ -1,521 +0,0 @@
|
|||
/*
|
||||
* ECC algorithm for M-systems disk on chip. We use the excellent Reed
|
||||
* Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
|
||||
* GNU GPL License. The rest is simply to convert the disk on chip
|
||||
* syndrome into a standard syndome.
|
||||
*
|
||||
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
|
||||
* Copyright (C) 2000 Netgem S.A.
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/doc2000.h>
|
||||
|
||||
#define DEBUG_ECC 0
|
||||
/* need to undef it (from asm/termbits.h) */
|
||||
#undef B0
|
||||
|
||||
#define MM 10 /* Symbol size in bits */
|
||||
#define KK (1023-4) /* Number of data symbols per block */
|
||||
#define B0 510 /* First root of generator polynomial, alpha form */
|
||||
#define PRIM 1 /* power of alpha used to generate roots of generator poly */
|
||||
#define NN ((1 << MM) - 1)
|
||||
|
||||
typedef unsigned short dtype;
|
||||
|
||||
/* 1+x^3+x^10 */
|
||||
static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
|
||||
|
||||
/* This defines the type used to store an element of the Galois Field
|
||||
* used by the code. Make sure this is something larger than a char if
|
||||
* if anything larger than GF(256) is used.
|
||||
*
|
||||
* Note: unsigned char will work up to GF(256) but int seems to run
|
||||
* faster on the Pentium.
|
||||
*/
|
||||
typedef int gf;
|
||||
|
||||
/* No legal value in index form represents zero, so
|
||||
* we need a special value for this purpose
|
||||
*/
|
||||
#define A0 (NN)
|
||||
|
||||
/* Compute x % NN, where NN is 2**MM - 1,
|
||||
* without a slow divide
|
||||
*/
|
||||
static inline gf
|
||||
modnn(int x)
|
||||
{
|
||||
while (x >= NN) {
|
||||
x -= NN;
|
||||
x = (x >> MM) + (x & NN);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
#define CLEAR(a,n) {\
|
||||
int ci;\
|
||||
for(ci=(n)-1;ci >=0;ci--)\
|
||||
(a)[ci] = 0;\
|
||||
}
|
||||
|
||||
#define COPY(a,b,n) {\
|
||||
int ci;\
|
||||
for(ci=(n)-1;ci >=0;ci--)\
|
||||
(a)[ci] = (b)[ci];\
|
||||
}
|
||||
|
||||
#define COPYDOWN(a,b,n) {\
|
||||
int ci;\
|
||||
for(ci=(n)-1;ci >=0;ci--)\
|
||||
(a)[ci] = (b)[ci];\
|
||||
}
|
||||
|
||||
#define Ldec 1
|
||||
|
||||
/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
|
||||
lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
|
||||
polynomial form -> index form index_of[j=alpha**i] = i
|
||||
alpha=2 is the primitive element of GF(2**m)
|
||||
HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
|
||||
Let @ represent the primitive element commonly called "alpha" that
|
||||
is the root of the primitive polynomial p(x). Then in GF(2^m), for any
|
||||
0 <= i <= 2^m-2,
|
||||
@^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
|
||||
where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
|
||||
of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
|
||||
example the polynomial representation of @^5 would be given by the binary
|
||||
representation of the integer "alpha_to[5]".
|
||||
Similarly, index_of[] can be used as follows:
|
||||
As above, let @ represent the primitive element of GF(2^m) that is
|
||||
the root of the primitive polynomial p(x). In order to find the power
|
||||
of @ (alpha) that has the polynomial representation
|
||||
a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
|
||||
we consider the integer "i" whose binary representation with a(0) being LSB
|
||||
and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
|
||||
"index_of[i]". Now, @^index_of[i] is that element whose polynomial
|
||||
representation is (a(0),a(1),a(2),...,a(m-1)).
|
||||
NOTE:
|
||||
The element alpha_to[2^m-1] = 0 always signifying that the
|
||||
representation of "@^infinity" = 0 is (0,0,0,...,0).
|
||||
Similarly, the element index_of[0] = A0 always signifying
|
||||
that the power of alpha which has the polynomial representation
|
||||
(0,0,...,0) is "infinity".
|
||||
|
||||
*/
|
||||
|
||||
static void
|
||||
generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1])
|
||||
{
|
||||
register int i, mask;
|
||||
|
||||
mask = 1;
|
||||
Alpha_to[MM] = 0;
|
||||
for (i = 0; i < MM; i++) {
|
||||
Alpha_to[i] = mask;
|
||||
Index_of[Alpha_to[i]] = i;
|
||||
/* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
|
||||
if (Pp[i] != 0)
|
||||
Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */
|
||||
mask <<= 1; /* single left-shift */
|
||||
}
|
||||
Index_of[Alpha_to[MM]] = MM;
|
||||
/*
|
||||
* Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
|
||||
* poly-repr of @^i shifted left one-bit and accounting for any @^MM
|
||||
* term that may occur when poly-repr of @^i is shifted.
|
||||
*/
|
||||
mask >>= 1;
|
||||
for (i = MM + 1; i < NN; i++) {
|
||||
if (Alpha_to[i - 1] >= mask)
|
||||
Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
|
||||
else
|
||||
Alpha_to[i] = Alpha_to[i - 1] << 1;
|
||||
Index_of[Alpha_to[i]] = i;
|
||||
}
|
||||
Index_of[0] = A0;
|
||||
Alpha_to[NN] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content
|
||||
* of the feedback shift register after having processed the data and
|
||||
* the ECC.
|
||||
*
|
||||
* Return number of symbols corrected, or -1 if codeword is illegal
|
||||
* or uncorrectable. If eras_pos is non-null, the detected error locations
|
||||
* are written back. NOTE! This array must be at least NN-KK elements long.
|
||||
* The corrected data are written in eras_val[]. They must be xor with the data
|
||||
* to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] .
|
||||
*
|
||||
* First "no_eras" erasures are declared by the calling program. Then, the
|
||||
* maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
|
||||
* If the number of channel errors is not greater than "t_after_eras" the
|
||||
* transmitted codeword will be recovered. Details of algorithm can be found
|
||||
* in R. Blahut's "Theory ... of Error-Correcting Codes".
|
||||
|
||||
* Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
|
||||
* will result. The decoder *could* check for this condition, but it would involve
|
||||
* extra time on every decoding operation.
|
||||
* */
|
||||
static int
|
||||
eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1],
|
||||
gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK],
|
||||
int no_eras)
|
||||
{
|
||||
int deg_lambda, el, deg_omega;
|
||||
int i, j, r,k;
|
||||
gf u,q,tmp,num1,num2,den,discr_r;
|
||||
gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly
|
||||
* and syndrome poly */
|
||||
gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
|
||||
gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
|
||||
int syn_error, count;
|
||||
|
||||
syn_error = 0;
|
||||
for(i=0;i<NN-KK;i++)
|
||||
syn_error |= bb[i];
|
||||
|
||||
if (!syn_error) {
|
||||
/* if remainder is zero, data[] is a codeword and there are no
|
||||
* errors to correct. So return data[] unmodified
|
||||
*/
|
||||
count = 0;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
for(i=1;i<=NN-KK;i++){
|
||||
s[i] = bb[0];
|
||||
}
|
||||
for(j=1;j<NN-KK;j++){
|
||||
if(bb[j] == 0)
|
||||
continue;
|
||||
tmp = Index_of[bb[j]];
|
||||
|
||||
for(i=1;i<=NN-KK;i++)
|
||||
s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
|
||||
}
|
||||
|
||||
/* undo the feedback register implicit multiplication and convert
|
||||
syndromes to index form */
|
||||
|
||||
for(i=1;i<=NN-KK;i++) {
|
||||
tmp = Index_of[s[i]];
|
||||
if (tmp != A0)
|
||||
tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM);
|
||||
s[i] = tmp;
|
||||
}
|
||||
|
||||
CLEAR(&lambda[1],NN-KK);
|
||||
lambda[0] = 1;
|
||||
|
||||
if (no_eras > 0) {
|
||||
/* Init lambda to be the erasure locator polynomial */
|
||||
lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
|
||||
for (i = 1; i < no_eras; i++) {
|
||||
u = modnn(PRIM*eras_pos[i]);
|
||||
for (j = i+1; j > 0; j--) {
|
||||
tmp = Index_of[lambda[j - 1]];
|
||||
if(tmp != A0)
|
||||
lambda[j] ^= Alpha_to[modnn(u + tmp)];
|
||||
}
|
||||
}
|
||||
#if DEBUG_ECC >= 1
|
||||
/* Test code that verifies the erasure locator polynomial just constructed
|
||||
Needed only for decoder debugging. */
|
||||
|
||||
/* find roots of the erasure location polynomial */
|
||||
for(i=1;i<=no_eras;i++)
|
||||
reg[i] = Index_of[lambda[i]];
|
||||
count = 0;
|
||||
for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
|
||||
q = 1;
|
||||
for (j = 1; j <= no_eras; j++)
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = modnn(reg[j] + j);
|
||||
q ^= Alpha_to[reg[j]];
|
||||
}
|
||||
if (q != 0)
|
||||
continue;
|
||||
/* store root and error location number indices */
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
count++;
|
||||
}
|
||||
if (count != no_eras) {
|
||||
printf("\n lambda(x) is WRONG\n");
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
#if DEBUG_ECC >= 2
|
||||
printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
|
||||
for (i = 0; i < count; i++)
|
||||
printf("%d ", loc[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
for(i=0;i<NN-KK+1;i++)
|
||||
b[i] = Index_of[lambda[i]];
|
||||
|
||||
/*
|
||||
* Begin Berlekamp-Massey algorithm to determine error+erasure
|
||||
* locator polynomial
|
||||
*/
|
||||
r = no_eras;
|
||||
el = no_eras;
|
||||
while (++r <= NN-KK) { /* r is the step number */
|
||||
/* Compute discrepancy at the r-th step in poly-form */
|
||||
discr_r = 0;
|
||||
for (i = 0; i < r; i++){
|
||||
if ((lambda[i] != 0) && (s[r - i] != A0)) {
|
||||
discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
|
||||
}
|
||||
}
|
||||
discr_r = Index_of[discr_r]; /* Index form */
|
||||
if (discr_r == A0) {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
COPYDOWN(&b[1],b,NN-KK);
|
||||
b[0] = A0;
|
||||
} else {
|
||||
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
|
||||
t[0] = lambda[0];
|
||||
for (i = 0 ; i < NN-KK; i++) {
|
||||
if(b[i] != A0)
|
||||
t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
|
||||
else
|
||||
t[i+1] = lambda[i+1];
|
||||
}
|
||||
if (2 * el <= r + no_eras - 1) {
|
||||
el = r + no_eras - el;
|
||||
/*
|
||||
* 2 lines below: B(x) <-- inv(discr_r) *
|
||||
* lambda(x)
|
||||
*/
|
||||
for (i = 0; i <= NN-KK; i++)
|
||||
b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
|
||||
} else {
|
||||
/* 2 lines below: B(x) <-- x*B(x) */
|
||||
COPYDOWN(&b[1],b,NN-KK);
|
||||
b[0] = A0;
|
||||
}
|
||||
COPY(lambda,t,NN-KK+1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert lambda to index form and compute deg(lambda(x)) */
|
||||
deg_lambda = 0;
|
||||
for(i=0;i<NN-KK+1;i++){
|
||||
lambda[i] = Index_of[lambda[i]];
|
||||
if(lambda[i] != A0)
|
||||
deg_lambda = i;
|
||||
}
|
||||
/*
|
||||
* Find roots of the error+erasure locator polynomial by Chien
|
||||
* Search
|
||||
*/
|
||||
COPY(®[1],&lambda[1],NN-KK);
|
||||
count = 0; /* Number of roots of lambda(x) */
|
||||
for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
|
||||
q = 1;
|
||||
for (j = deg_lambda; j > 0; j--){
|
||||
if (reg[j] != A0) {
|
||||
reg[j] = modnn(reg[j] + j);
|
||||
q ^= Alpha_to[reg[j]];
|
||||
}
|
||||
}
|
||||
if (q != 0)
|
||||
continue;
|
||||
/* store root (index-form) and error location number */
|
||||
root[count] = i;
|
||||
loc[count] = k;
|
||||
/* If we've already found max possible roots,
|
||||
* abort the search to save time
|
||||
*/
|
||||
if(++count == deg_lambda)
|
||||
break;
|
||||
}
|
||||
if (deg_lambda != count) {
|
||||
/*
|
||||
* deg(lambda) unequal to number of roots => uncorrectable
|
||||
* error detected
|
||||
*/
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
/*
|
||||
* Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
|
||||
* x**(NN-KK)). in index form. Also find deg(omega).
|
||||
*/
|
||||
deg_omega = 0;
|
||||
for (i = 0; i < NN-KK;i++){
|
||||
tmp = 0;
|
||||
j = (deg_lambda < i) ? deg_lambda : i;
|
||||
for(;j >= 0; j--){
|
||||
if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
|
||||
tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
|
||||
}
|
||||
if(tmp != 0)
|
||||
deg_omega = i;
|
||||
omega[i] = Index_of[tmp];
|
||||
}
|
||||
omega[NN-KK] = A0;
|
||||
|
||||
/*
|
||||
* Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
|
||||
* inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
|
||||
*/
|
||||
for (j = count-1; j >=0; j--) {
|
||||
num1 = 0;
|
||||
for (i = deg_omega; i >= 0; i--) {
|
||||
if (omega[i] != A0)
|
||||
num1 ^= Alpha_to[modnn(omega[i] + i * root[j])];
|
||||
}
|
||||
num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
|
||||
den = 0;
|
||||
|
||||
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
|
||||
for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
|
||||
if(lambda[i+1] != A0)
|
||||
den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
|
||||
}
|
||||
if (den == 0) {
|
||||
#if DEBUG_ECC >= 1
|
||||
printf("\n ERROR: denominator = 0\n");
|
||||
#endif
|
||||
/* Convert to dual- basis */
|
||||
count = -1;
|
||||
goto finish;
|
||||
}
|
||||
/* Apply error to data */
|
||||
if (num1 != 0) {
|
||||
eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
|
||||
} else {
|
||||
eras_val[j] = 0;
|
||||
}
|
||||
}
|
||||
finish:
|
||||
for(i=0;i<count;i++)
|
||||
eras_pos[i] = loc[i];
|
||||
return count;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/* The DOC specific code begins here */
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
/* The sector bytes are packed into NB_DATA MM bits words */
|
||||
#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM)
|
||||
|
||||
/*
|
||||
* Correct the errors in 'sector[]' by using 'ecc1[]' which is the
|
||||
* content of the feedback shift register applyied to the sector and
|
||||
* the ECC. Return the number of errors corrected (and correct them in
|
||||
* sector), or -1 if error
|
||||
*/
|
||||
int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6])
|
||||
{
|
||||
int parity, i, nb_errors;
|
||||
gf bb[NN - KK + 1];
|
||||
gf error_val[NN-KK];
|
||||
int error_pos[NN-KK], pos, bitpos, index, val;
|
||||
dtype *Alpha_to, *Index_of;
|
||||
|
||||
/* init log and exp tables here to save memory. However, it is slower */
|
||||
Alpha_to = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
|
||||
if (!Alpha_to)
|
||||
return -1;
|
||||
|
||||
Index_of = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
|
||||
if (!Index_of) {
|
||||
kfree(Alpha_to);
|
||||
return -1;
|
||||
}
|
||||
|
||||
generate_gf(Alpha_to, Index_of);
|
||||
|
||||
parity = ecc1[1];
|
||||
|
||||
bb[0] = (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8);
|
||||
bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6);
|
||||
bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4);
|
||||
bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2);
|
||||
|
||||
nb_errors = eras_dec_rs(Alpha_to, Index_of, bb,
|
||||
error_val, error_pos, 0);
|
||||
if (nb_errors <= 0)
|
||||
goto the_end;
|
||||
|
||||
/* correct the errors */
|
||||
for(i=0;i<nb_errors;i++) {
|
||||
pos = error_pos[i];
|
||||
if (pos >= NB_DATA && pos < KK) {
|
||||
nb_errors = -1;
|
||||
goto the_end;
|
||||
}
|
||||
if (pos < NB_DATA) {
|
||||
/* extract bit position (MSB first) */
|
||||
pos = 10 * (NB_DATA - 1 - pos) - 6;
|
||||
/* now correct the following 10 bits. At most two bytes
|
||||
can be modified since pos is even */
|
||||
index = (pos >> 3) ^ 1;
|
||||
bitpos = pos & 7;
|
||||
if ((index >= 0 && index < SECTOR_SIZE) ||
|
||||
index == (SECTOR_SIZE + 1)) {
|
||||
val = error_val[i] >> (2 + bitpos);
|
||||
parity ^= val;
|
||||
if (index < SECTOR_SIZE)
|
||||
sector[index] ^= val;
|
||||
}
|
||||
index = ((pos >> 3) + 1) ^ 1;
|
||||
bitpos = (bitpos + 10) & 7;
|
||||
if (bitpos == 0)
|
||||
bitpos = 8;
|
||||
if ((index >= 0 && index < SECTOR_SIZE) ||
|
||||
index == (SECTOR_SIZE + 1)) {
|
||||
val = error_val[i] << (8 - bitpos);
|
||||
parity ^= val;
|
||||
if (index < SECTOR_SIZE)
|
||||
sector[index] ^= val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* use parity to test extra errors */
|
||||
if ((parity & 0xff) != 0)
|
||||
nb_errors = -1;
|
||||
|
||||
the_end:
|
||||
kfree(Alpha_to);
|
||||
kfree(Index_of);
|
||||
return nb_errors;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(doc_decode_ecc);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>");
|
||||
MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware");
|
|
@ -123,7 +123,7 @@ static inline void doc_flash_address(struct docg3 *docg3, u8 addr)
|
|||
doc_writeb(docg3, addr, DOC_FLASHADDRESS);
|
||||
}
|
||||
|
||||
static char const *part_probes[] = { "cmdlinepart", "saftlpart", NULL };
|
||||
static char const * const part_probes[] = { "cmdlinepart", "saftlpart", NULL };
|
||||
|
||||
static int doc_register_readb(struct docg3 *docg3, int reg)
|
||||
{
|
||||
|
@ -2144,18 +2144,7 @@ static struct platform_driver g3_driver = {
|
|||
.remove = __exit_p(docg3_release),
|
||||
};
|
||||
|
||||
static int __init docg3_init(void)
|
||||
{
|
||||
return platform_driver_probe(&g3_driver, docg3_probe);
|
||||
}
|
||||
module_init(docg3_init);
|
||||
|
||||
|
||||
static void __exit docg3_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&g3_driver);
|
||||
}
|
||||
module_exit(docg3_exit);
|
||||
module_platform_driver_probe(g3_driver, docg3_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
|
||||
|
|
|
@ -1,325 +0,0 @@
|
|||
|
||||
/* Linux driver for Disk-On-Chip devices */
|
||||
/* Probe routines common to all DoC devices */
|
||||
/* (C) 1999 Machine Vision Holdings, Inc. */
|
||||
/* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */
|
||||
|
||||
|
||||
/* DOC_PASSIVE_PROBE:
|
||||
In order to ensure that the BIOS checksum is correct at boot time, and
|
||||
hence that the onboard BIOS extension gets executed, the DiskOnChip
|
||||
goes into reset mode when it is read sequentially: all registers
|
||||
return 0xff until the chip is woken up again by writing to the
|
||||
DOCControl register.
|
||||
|
||||
Unfortunately, this means that the probe for the DiskOnChip is unsafe,
|
||||
because one of the first things it does is write to where it thinks
|
||||
the DOCControl register should be - which may well be shared memory
|
||||
for another device. I've had machines which lock up when this is
|
||||
attempted. Hence the possibility to do a passive probe, which will fail
|
||||
to detect a chip in reset mode, but is at least guaranteed not to lock
|
||||
the machine.
|
||||
|
||||
If you have this problem, uncomment the following line:
|
||||
#define DOC_PASSIVE_PROBE
|
||||
*/
|
||||
|
||||
|
||||
/* DOC_SINGLE_DRIVER:
|
||||
Millennium driver has been merged into DOC2000 driver.
|
||||
|
||||
The old Millennium-only driver has been retained just in case there
|
||||
are problems with the new code. If the combined driver doesn't work
|
||||
for you, you can try the old one by undefining DOC_SINGLE_DRIVER
|
||||
below and also enabling it in your configuration. If this fixes the
|
||||
problems, please send a report to the MTD mailing list at
|
||||
<linux-mtd@lists.infradead.org>.
|
||||
*/
|
||||
#define DOC_SINGLE_DRIVER
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/doc2000.h>
|
||||
|
||||
|
||||
static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
|
||||
module_param(doc_config_location, ulong, 0);
|
||||
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
|
||||
|
||||
static unsigned long __initdata doc_locations[] = {
|
||||
#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
|
||||
#ifdef CONFIG_MTD_DOCPROBE_HIGH
|
||||
0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
|
||||
0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
|
||||
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
|
||||
0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
|
||||
0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
|
||||
#else /* CONFIG_MTD_DOCPROBE_HIGH */
|
||||
0xc8000, 0xca000, 0xcc000, 0xce000,
|
||||
0xd0000, 0xd2000, 0xd4000, 0xd6000,
|
||||
0xd8000, 0xda000, 0xdc000, 0xde000,
|
||||
0xe0000, 0xe2000, 0xe4000, 0xe6000,
|
||||
0xe8000, 0xea000, 0xec000, 0xee000,
|
||||
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
|
||||
#endif
|
||||
0xffffffff };
|
||||
|
||||
/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
|
||||
|
||||
static inline int __init doccheck(void __iomem *potential, unsigned long physadr)
|
||||
{
|
||||
void __iomem *window=potential;
|
||||
unsigned char tmp, tmpb, tmpc, ChipID;
|
||||
#ifndef DOC_PASSIVE_PROBE
|
||||
unsigned char tmp2;
|
||||
#endif
|
||||
|
||||
/* Routine copied from the Linux DOC driver */
|
||||
|
||||
#ifdef CONFIG_MTD_DOCPROBE_55AA
|
||||
/* Check for 0x55 0xAA signature at beginning of window,
|
||||
this is no longer true once we remove the IPL (for Millennium */
|
||||
if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa)
|
||||
return 0;
|
||||
#endif /* CONFIG_MTD_DOCPROBE_55AA */
|
||||
|
||||
#ifndef DOC_PASSIVE_PROBE
|
||||
/* It's not possible to cleanly detect the DiskOnChip - the
|
||||
* bootup procedure will put the device into reset mode, and
|
||||
* it's not possible to talk to it without actually writing
|
||||
* to the DOCControl register. So we store the current contents
|
||||
* of the DOCControl register's location, in case we later decide
|
||||
* that it's not a DiskOnChip, and want to put it back how we
|
||||
* found it.
|
||||
*/
|
||||
tmp2 = ReadDOC(window, DOCControl);
|
||||
|
||||
/* Reset the DiskOnChip ASIC */
|
||||
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
|
||||
window, DOCControl);
|
||||
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
|
||||
window, DOCControl);
|
||||
|
||||
/* Enable the DiskOnChip ASIC */
|
||||
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
|
||||
window, DOCControl);
|
||||
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
|
||||
window, DOCControl);
|
||||
#endif /* !DOC_PASSIVE_PROBE */
|
||||
|
||||
/* We need to read the ChipID register four times. For some
|
||||
newer DiskOnChip 2000 units, the first three reads will
|
||||
return the DiskOnChip Millennium ident. Don't ask. */
|
||||
ChipID = ReadDOC(window, ChipID);
|
||||
|
||||
switch (ChipID) {
|
||||
case DOC_ChipID_Doc2k:
|
||||
/* Check the TOGGLE bit in the ECC register */
|
||||
tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
|
||||
tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
|
||||
tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
|
||||
if (tmp != tmpb && tmp == tmpc)
|
||||
return ChipID;
|
||||
break;
|
||||
|
||||
case DOC_ChipID_DocMil:
|
||||
/* Check for the new 2000 with Millennium ASIC */
|
||||
ReadDOC(window, ChipID);
|
||||
ReadDOC(window, ChipID);
|
||||
if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil)
|
||||
ChipID = DOC_ChipID_Doc2kTSOP;
|
||||
|
||||
/* Check the TOGGLE bit in the ECC register */
|
||||
tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
|
||||
tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
|
||||
tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
|
||||
if (tmp != tmpb && tmp == tmpc)
|
||||
return ChipID;
|
||||
break;
|
||||
|
||||
case DOC_ChipID_DocMilPlus16:
|
||||
case DOC_ChipID_DocMilPlus32:
|
||||
case 0:
|
||||
/* Possible Millennium+, need to do more checks */
|
||||
#ifndef DOC_PASSIVE_PROBE
|
||||
/* Possibly release from power down mode */
|
||||
for (tmp = 0; (tmp < 4); tmp++)
|
||||
ReadDOC(window, Mplus_Power);
|
||||
|
||||
/* Reset the DiskOnChip ASIC */
|
||||
tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
|
||||
DOC_MODE_BDECT;
|
||||
WriteDOC(tmp, window, Mplus_DOCControl);
|
||||
WriteDOC(~tmp, window, Mplus_CtrlConfirm);
|
||||
|
||||
mdelay(1);
|
||||
/* Enable the DiskOnChip ASIC */
|
||||
tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
|
||||
DOC_MODE_BDECT;
|
||||
WriteDOC(tmp, window, Mplus_DOCControl);
|
||||
WriteDOC(~tmp, window, Mplus_CtrlConfirm);
|
||||
mdelay(1);
|
||||
#endif /* !DOC_PASSIVE_PROBE */
|
||||
|
||||
ChipID = ReadDOC(window, ChipID);
|
||||
|
||||
switch (ChipID) {
|
||||
case DOC_ChipID_DocMilPlus16:
|
||||
case DOC_ChipID_DocMilPlus32:
|
||||
/* Check the TOGGLE bit in the toggle register */
|
||||
tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
|
||||
tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
|
||||
tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
|
||||
if (tmp != tmpb && tmp == tmpc)
|
||||
return ChipID;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* FALL TRHU */
|
||||
|
||||
default:
|
||||
|
||||
#ifdef CONFIG_MTD_DOCPROBE_55AA
|
||||
printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
|
||||
ChipID, physadr);
|
||||
#endif
|
||||
#ifndef DOC_PASSIVE_PROBE
|
||||
/* Put back the contents of the DOCControl register, in case it's not
|
||||
* actually a DiskOnChip.
|
||||
*/
|
||||
WriteDOC(tmp2, window, DOCControl);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_WARNING "DiskOnChip failed TOGGLE test, dropping.\n");
|
||||
|
||||
#ifndef DOC_PASSIVE_PROBE
|
||||
/* Put back the contents of the DOCControl register: it's not a DiskOnChip */
|
||||
WriteDOC(tmp2, window, DOCControl);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int docfound;
|
||||
|
||||
extern void DoC2k_init(struct mtd_info *);
|
||||
extern void DoCMil_init(struct mtd_info *);
|
||||
extern void DoCMilPlus_init(struct mtd_info *);
|
||||
|
||||
static void __init DoC_Probe(unsigned long physadr)
|
||||
{
|
||||
void __iomem *docptr;
|
||||
struct DiskOnChip *this;
|
||||
struct mtd_info *mtd;
|
||||
int ChipID;
|
||||
char namebuf[15];
|
||||
char *name = namebuf;
|
||||
void (*initroutine)(struct mtd_info *) = NULL;
|
||||
|
||||
docptr = ioremap(physadr, DOC_IOREMAP_LEN);
|
||||
|
||||
if (!docptr)
|
||||
return;
|
||||
|
||||
if ((ChipID = doccheck(docptr, physadr))) {
|
||||
if (ChipID == DOC_ChipID_Doc2kTSOP) {
|
||||
/* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */
|
||||
printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n");
|
||||
iounmap(docptr);
|
||||
return;
|
||||
}
|
||||
docfound = 1;
|
||||
mtd = kzalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
|
||||
if (!mtd) {
|
||||
printk(KERN_WARNING "Cannot allocate memory for data structures. Dropping.\n");
|
||||
iounmap(docptr);
|
||||
return;
|
||||
}
|
||||
|
||||
this = (struct DiskOnChip *)(&mtd[1]);
|
||||
mtd->priv = this;
|
||||
this->virtadr = docptr;
|
||||
this->physadr = physadr;
|
||||
this->ChipID = ChipID;
|
||||
sprintf(namebuf, "with ChipID %2.2X", ChipID);
|
||||
|
||||
switch(ChipID) {
|
||||
case DOC_ChipID_Doc2kTSOP:
|
||||
name="2000 TSOP";
|
||||
initroutine = symbol_request(DoC2k_init);
|
||||
break;
|
||||
|
||||
case DOC_ChipID_Doc2k:
|
||||
name="2000";
|
||||
initroutine = symbol_request(DoC2k_init);
|
||||
break;
|
||||
|
||||
case DOC_ChipID_DocMil:
|
||||
name="Millennium";
|
||||
#ifdef DOC_SINGLE_DRIVER
|
||||
initroutine = symbol_request(DoC2k_init);
|
||||
#else
|
||||
initroutine = symbol_request(DoCMil_init);
|
||||
#endif /* DOC_SINGLE_DRIVER */
|
||||
break;
|
||||
|
||||
case DOC_ChipID_DocMilPlus16:
|
||||
case DOC_ChipID_DocMilPlus32:
|
||||
name="MillenniumPlus";
|
||||
initroutine = symbol_request(DoCMilPlus_init);
|
||||
break;
|
||||
}
|
||||
|
||||
if (initroutine) {
|
||||
(*initroutine)(mtd);
|
||||
symbol_put_addr(initroutine);
|
||||
return;
|
||||
}
|
||||
printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
|
||||
kfree(mtd);
|
||||
}
|
||||
iounmap(docptr);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Module stuff
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int __init init_doc(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (doc_config_location) {
|
||||
printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
|
||||
DoC_Probe(doc_config_location);
|
||||
} else {
|
||||
for (i=0; (doc_locations[i] != 0xffffffff); i++) {
|
||||
DoC_Probe(doc_locations[i]);
|
||||
}
|
||||
}
|
||||
/* No banner message any more. Print a message if no DiskOnChip
|
||||
found, so the user knows we at least tried. */
|
||||
if (!docfound)
|
||||
printk(KERN_INFO "No recognised DiskOnChip devices found\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
module_init(init_doc);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
MODULE_DESCRIPTION("Probe code for DiskOnChip 2000 and Millennium devices");
|
||||
|
|
@ -81,14 +81,21 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
|
|||
* @dev: ELM device
|
||||
* @bch_type: Type of BCH ecc
|
||||
*/
|
||||
void elm_config(struct device *dev, enum bch_ecc bch_type)
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type)
|
||||
{
|
||||
u32 reg_val;
|
||||
struct elm_info *info = dev_get_drvdata(dev);
|
||||
|
||||
if (!info) {
|
||||
dev_err(dev, "Unable to configure elm - device not probed?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
|
||||
elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
|
||||
info->bch_type = bch_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(elm_config);
|
||||
|
||||
|
|
|
@ -681,6 +681,7 @@ struct flash_info {
|
|||
u16 flags;
|
||||
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
||||
#define M25P_NO_ERASE 0x02 /* No erase command needed */
|
||||
#define SST_WRITE 0x04 /* use SST byte programming */
|
||||
};
|
||||
|
||||
#define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \
|
||||
|
@ -728,6 +729,7 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
|
||||
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
|
||||
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) },
|
||||
|
||||
/* Everspin */
|
||||
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) },
|
||||
|
@ -740,7 +742,6 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "160s33b", INFO(0x898911, 0, 64 * 1024, 32, 0) },
|
||||
{ "320s33b", INFO(0x898912, 0, 64 * 1024, 64, 0) },
|
||||
{ "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) },
|
||||
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
|
||||
|
||||
/* Macronix */
|
||||
{ "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 4, SECT_4K) },
|
||||
|
@ -753,8 +754,10 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) },
|
||||
|
||||
/* Micron */
|
||||
{ "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) },
|
||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) },
|
||||
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) },
|
||||
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) },
|
||||
|
@ -781,14 +784,15 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K) },
|
||||
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K) },
|
||||
{ "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K) },
|
||||
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K) },
|
||||
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K) },
|
||||
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K) },
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||
{ "sst25vf080b", INFO(0xbf258e, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
|
||||
{ "sst25vf016b", INFO(0xbf2541, 0, 64 * 1024, 32, SECT_4K | SST_WRITE) },
|
||||
{ "sst25vf032b", INFO(0xbf254a, 0, 64 * 1024, 64, SECT_4K | SST_WRITE) },
|
||||
{ "sst25vf064c", INFO(0xbf254b, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "sst25wf512", INFO(0xbf2501, 0, 64 * 1024, 1, SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf010", INFO(0xbf2502, 0, 64 * 1024, 2, SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf020", INFO(0xbf2503, 0, 64 * 1024, 4, SECT_4K | SST_WRITE) },
|
||||
{ "sst25wf040", INFO(0xbf2504, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||
|
||||
/* ST Microelectronics -- newer production may have feature updates */
|
||||
{ "m25p05", INFO(0x202010, 0, 32 * 1024, 2, 0) },
|
||||
|
@ -838,6 +842,7 @@ static const struct spi_device_id m25p_ids[] = {
|
|||
{ "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
||||
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
|
||||
|
||||
/* Catalyst / On Semiconductor -- non-JEDEC */
|
||||
|
@ -1000,7 +1005,7 @@ static int m25p_probe(struct spi_device *spi)
|
|||
}
|
||||
|
||||
/* sst flash chips use AAI word program */
|
||||
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
|
||||
if (info->flags & SST_WRITE)
|
||||
flash->mtd._write = sst_write;
|
||||
else
|
||||
flash->mtd._write = m25p80_write;
|
||||
|
|
|
@ -105,8 +105,6 @@ static const struct of_device_id dataflash_dt_ids[] = {
|
|||
{ .compatible = "atmel,dataflash", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
#else
|
||||
#define dataflash_dt_ids NULL
|
||||
#endif
|
||||
|
||||
/* ......................................................................... */
|
||||
|
@ -914,7 +912,7 @@ static struct spi_driver dataflash_driver = {
|
|||
.driver = {
|
||||
.name = "mtd_dataflash",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = dataflash_dt_ids,
|
||||
.of_match_table = of_match_ptr(dataflash_dt_ids),
|
||||
},
|
||||
|
||||
.probe = dataflash_probe,
|
||||
|
|
|
@ -249,22 +249,6 @@ config MTD_LANTIQ
|
|||
help
|
||||
Support for NOR flash attached to the Lantiq SoC's External Bus Unit.
|
||||
|
||||
config MTD_DILNETPC
|
||||
tristate "CFI Flash device mapped on DIL/Net PC"
|
||||
depends on X86 && MTD_CFI_INTELEXT && BROKEN
|
||||
help
|
||||
MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP".
|
||||
For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm>
|
||||
and <http://www.ssv-embedded.de/ssv/pc104/p170.htm>
|
||||
|
||||
config MTD_DILNETPC_BOOTSIZE
|
||||
hex "Size of DIL/Net PC flash boot partition"
|
||||
depends on MTD_DILNETPC
|
||||
default "0x80000"
|
||||
help
|
||||
The amount of space taken up by the kernel or Etherboot
|
||||
on the DIL/Net PC flash chips.
|
||||
|
||||
config MTD_L440GX
|
||||
tristate "BIOS flash chip on Intel L440GX boards"
|
||||
depends on X86 && MTD_JEDECPROBE
|
||||
|
@ -274,42 +258,6 @@ config MTD_L440GX
|
|||
|
||||
BE VERY CAREFUL.
|
||||
|
||||
config MTD_TQM8XXL
|
||||
tristate "CFI Flash device mapped on TQM8XXL"
|
||||
depends on MTD_CFI && TQM8xxL
|
||||
help
|
||||
The TQM8xxL PowerPC board has up to two banks of CFI-compliant
|
||||
chips, currently uses AMD one. This 'mapping' driver supports
|
||||
that arrangement, allowing the CFI probe and command set driver
|
||||
code to communicate with the chips on the TQM8xxL board. More at
|
||||
<http://www.denx.de/wiki/PPCEmbedded/>.
|
||||
|
||||
config MTD_RPXLITE
|
||||
tristate "CFI Flash device mapped on RPX Lite or CLLF"
|
||||
depends on MTD_CFI && (RPXCLASSIC || RPXLITE)
|
||||
help
|
||||
The RPXLite PowerPC board has CFI-compliant chips mapped in
|
||||
a strange sparse mapping. This 'mapping' driver supports that
|
||||
arrangement, allowing the CFI probe and command set driver code
|
||||
to communicate with the chips on the RPXLite board. More at
|
||||
<http://www.embeddedplanet.com/>.
|
||||
|
||||
config MTD_MBX860
|
||||
tristate "System flash on MBX860 board"
|
||||
depends on MTD_CFI && MBX
|
||||
help
|
||||
This enables access routines for the flash chips on the Motorola
|
||||
MBX860 board. If you have one of these boards and would like
|
||||
to use the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_DBOX2
|
||||
tristate "CFI Flash device mapped on D-Box2"
|
||||
depends on DBOX2 && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD
|
||||
help
|
||||
This enables access routines for the flash chips on the Nokia/Sagem
|
||||
D-Box 2 board. If you have one of these boards and would like to use
|
||||
the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_CFI_FLAGADM
|
||||
tristate "CFI Flash device mapping on FlagaDM"
|
||||
depends on 8xx && MTD_CFI
|
||||
|
@ -349,15 +297,6 @@ config MTD_IXP4XX
|
|||
IXDP425 and Coyote. If you have an IXP4xx based board and
|
||||
would like to use the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_IXP2000
|
||||
tristate "CFI Flash device mapped on Intel IXP2000 based systems"
|
||||
depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000
|
||||
help
|
||||
This enables MTD access to flash devices on platforms based
|
||||
on Intel's IXP2000 family of network processors. If you have an
|
||||
IXP2000 based board and would like to use the flash chips on it,
|
||||
say 'Y'.
|
||||
|
||||
config MTD_AUTCPU12
|
||||
bool "NV-RAM mapping AUTCPU12 board"
|
||||
depends on ARCH_AUTCPU12
|
||||
|
@ -372,13 +311,6 @@ config MTD_IMPA7
|
|||
This enables access to the NOR Flash on the impA7 board of
|
||||
implementa GmbH. If you have such a board, say 'Y' here.
|
||||
|
||||
config MTD_H720X
|
||||
tristate "Hynix evaluation board mappings"
|
||||
depends on MTD_CFI && ( ARCH_H7201 || ARCH_H7202 )
|
||||
help
|
||||
This enables access to the flash chips on the Hynix evaluation boards.
|
||||
If you have such a board, say 'Y'.
|
||||
|
||||
# This needs CFI or JEDEC, depending on the cards found.
|
||||
config MTD_PCI
|
||||
tristate "PCI MTD driver"
|
||||
|
@ -433,15 +365,6 @@ config MTD_UCLINUX
|
|||
help
|
||||
Map driver to support image based filesystems for uClinux.
|
||||
|
||||
config MTD_DMV182
|
||||
tristate "Map driver for Dy-4 SVME/DMV-182 board."
|
||||
depends on DMV182
|
||||
select MTD_MAP_BANK_WIDTH_32
|
||||
select MTD_CFI_I8
|
||||
select MTD_CFI_AMDSTD
|
||||
help
|
||||
Map driver for Dy-4 SVME/DMV-182 board.
|
||||
|
||||
config MTD_INTEL_VR_NOR
|
||||
tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
|
||||
depends on PCI
|
||||
|
|
|
@ -9,7 +9,6 @@ endif
|
|||
# Chip mappings
|
||||
obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o
|
||||
obj-$(CONFIG_MTD_DC21285) += dc21285.o
|
||||
obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o
|
||||
obj-$(CONFIG_MTD_L440GX) += l440gx.o
|
||||
obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
|
||||
obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
|
||||
|
@ -17,15 +16,12 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
|
|||
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
|
||||
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
||||
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
|
||||
obj-$(CONFIG_MTD_MBX860) += mbx860.o
|
||||
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
||||
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
|
||||
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
|
||||
obj-$(CONFIG_MTD_PISMO) += pismo.o
|
||||
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
|
||||
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
|
||||
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
|
||||
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o
|
||||
obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o
|
||||
obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o
|
||||
obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
|
||||
|
@ -34,7 +30,6 @@ obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o
|
|||
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
|
||||
obj-$(CONFIG_MTD_VMAX) += vmax301.o
|
||||
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
|
||||
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
|
||||
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
|
||||
obj-$(CONFIG_MTD_PCI) += pci.o
|
||||
obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
|
||||
|
@ -42,10 +37,7 @@ obj-$(CONFIG_MTD_IMPA7) += impa7.o
|
|||
obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
|
||||
obj-$(CONFIG_MTD_NETtel) += nettel.o
|
||||
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
|
||||
obj-$(CONFIG_MTD_H720X) += h720x-flash.o
|
||||
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
|
||||
obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
|
||||
obj-$(CONFIG_MTD_DMV182) += dmv182.o
|
||||
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
||||
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
|
||||
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
|
||||
|
|
|
@ -122,7 +122,8 @@ static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const voi
|
|||
switch_back(state);
|
||||
}
|
||||
|
||||
static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
|
||||
static const char * const part_probe_types[] = {
|
||||
"cmdlinepart", "RedBoot", NULL };
|
||||
|
||||
static int bfin_flash_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
|
|
@ -308,8 +308,7 @@ static int ck804xrom_init_one(struct pci_dev *pdev,
|
|||
|
||||
out:
|
||||
/* Free any left over map structures */
|
||||
if (map)
|
||||
kfree(map);
|
||||
kfree(map);
|
||||
|
||||
/* See if I have any map structures */
|
||||
if (list_empty(&window->maps)) {
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* D-Box 2 flash driver
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
/* partition_info gives details on the logical partitions that the split the
|
||||
* single flash device into. If the size if zero we use up to the end of the
|
||||
* device. */
|
||||
static struct mtd_partition partition_info[]= {
|
||||
{
|
||||
.name = "BR bootloader",
|
||||
.size = 128 * 1024,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},
|
||||
{
|
||||
.name = "FLFS (U-Boot)",
|
||||
.size = 128 * 1024,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = 0
|
||||
},
|
||||
{
|
||||
.name = "Root (SquashFS)",
|
||||
.size = 7040 * 1024,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = 0
|
||||
},
|
||||
{
|
||||
.name = "var (JFFS2)",
|
||||
.size = 896 * 1024,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = 0
|
||||
},
|
||||
{
|
||||
.name = "Flash without bootloader",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 128 * 1024,
|
||||
.mask_flags = 0
|
||||
},
|
||||
{
|
||||
.name = "Complete Flash",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
|
||||
|
||||
#define WINDOW_ADDR 0x10000000
|
||||
#define WINDOW_SIZE 0x800000
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
|
||||
|
||||
struct map_info dbox2_flash_map = {
|
||||
.name = "D-Box 2 flash memory",
|
||||
.size = WINDOW_SIZE,
|
||||
.bankwidth = 4,
|
||||
.phys = WINDOW_ADDR,
|
||||
};
|
||||
|
||||
static int __init init_dbox2_flash(void)
|
||||
{
|
||||
printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR);
|
||||
dbox2_flash_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
|
||||
|
||||
if (!dbox2_flash_map.virt) {
|
||||
printk("Failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
simple_map_init(&dbox2_flash_map);
|
||||
|
||||
// Probe for dual Intel 28F320 or dual AMD
|
||||
mymtd = do_map_probe("cfi_probe", &dbox2_flash_map);
|
||||
if (!mymtd) {
|
||||
// Probe for single Intel 28F640
|
||||
dbox2_flash_map.bankwidth = 2;
|
||||
|
||||
mymtd = do_map_probe("cfi_probe", &dbox2_flash_map);
|
||||
}
|
||||
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
|
||||
/* Create MTD devices for each partition. */
|
||||
mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap((void *)dbox2_flash_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void __exit cleanup_dbox2_flash(void)
|
||||
{
|
||||
if (mymtd) {
|
||||
mtd_device_unregister(mymtd);
|
||||
map_destroy(mymtd);
|
||||
}
|
||||
if (dbox2_flash_map.virt) {
|
||||
iounmap((void *)dbox2_flash_map.virt);
|
||||
dbox2_flash_map.virt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_dbox2_flash);
|
||||
module_exit(cleanup_dbox2_flash);
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
|
||||
MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
|
|
@ -143,9 +143,8 @@ static struct map_info dc21285_map = {
|
|||
.copy_from = dc21285_copy_from,
|
||||
};
|
||||
|
||||
|
||||
/* Partition stuff */
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int __init init_dc21285(void)
|
||||
{
|
||||
|
|
|
@ -1,496 +0,0 @@
|
|||
/* dilnetpc.c -- MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP"
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
|
||||
* featuring the AMD Elan SC410 processor. There are two variants of this
|
||||
* board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash
|
||||
* ROM (Intel 28F016S3) and 8 megs of DRAM, the ADNP version has 4 megs
|
||||
* flash and 16 megs of RAM.
|
||||
* For details, see http://www.ssv-embedded.de/ssv/pc104/p169.htm
|
||||
* and http://www.ssv-embedded.de/ssv/pc104/p170.htm
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
** The DIL/NetPC keeps its BIOS in two distinct flash blocks.
|
||||
** Destroying any of these blocks transforms the DNPC into
|
||||
** a paperweight (albeit not a very useful one, considering
|
||||
** it only weighs a few grams).
|
||||
**
|
||||
** Therefore, the BIOS blocks must never be erased or written to
|
||||
** except by people who know exactly what they are doing (e.g.
|
||||
** to install a BIOS update). These partitions are marked read-only
|
||||
** by default, but can be made read/write by undefining
|
||||
** DNPC_BIOS_BLOCKS_WRITEPROTECTED:
|
||||
*/
|
||||
#define DNPC_BIOS_BLOCKS_WRITEPROTECTED
|
||||
|
||||
/*
|
||||
** The ID string (in ROM) is checked to determine whether we
|
||||
** are running on a DNP/1486 or ADNP/1486
|
||||
*/
|
||||
#define BIOSID_BASE 0x000fe100
|
||||
|
||||
#define ID_DNPC "DNP1486"
|
||||
#define ID_ADNP "ADNP1486"
|
||||
|
||||
/*
|
||||
** Address where the flash should appear in CPU space
|
||||
*/
|
||||
#define FLASH_BASE 0x2000000
|
||||
|
||||
/*
|
||||
** Chip Setup and Control (CSC) indexed register space
|
||||
*/
|
||||
#define CSC_INDEX 0x22
|
||||
#define CSC_DATA 0x23
|
||||
|
||||
#define CSC_MMSWAR 0x30 /* MMS window C-F attributes register */
|
||||
#define CSC_MMSWDSR 0x31 /* MMS window C-F device select register */
|
||||
|
||||
#define CSC_RBWR 0xa7 /* GPIO Read-Back/Write Register B */
|
||||
|
||||
#define CSC_CR 0xd0 /* internal I/O device disable/Echo */
|
||||
/* Z-bus/configuration register */
|
||||
|
||||
#define CSC_PCCMDCR 0xf1 /* PC card mode and DMA control register */
|
||||
|
||||
|
||||
/*
|
||||
** PC Card indexed register space:
|
||||
*/
|
||||
|
||||
#define PCC_INDEX 0x3e0
|
||||
#define PCC_DATA 0x3e1
|
||||
|
||||
#define PCC_AWER_B 0x46 /* Socket B Address Window enable register */
|
||||
#define PCC_MWSAR_1_Lo 0x58 /* memory window 1 start address low register */
|
||||
#define PCC_MWSAR_1_Hi 0x59 /* memory window 1 start address high register */
|
||||
#define PCC_MWEAR_1_Lo 0x5A /* memory window 1 stop address low register */
|
||||
#define PCC_MWEAR_1_Hi 0x5B /* memory window 1 stop address high register */
|
||||
#define PCC_MWAOR_1_Lo 0x5C /* memory window 1 address offset low register */
|
||||
#define PCC_MWAOR_1_Hi 0x5D /* memory window 1 address offset high register */
|
||||
|
||||
|
||||
/*
|
||||
** Access to SC4x0's Chip Setup and Control (CSC)
|
||||
** and PC Card (PCC) indexed registers:
|
||||
*/
|
||||
static inline void setcsc(int reg, unsigned char data)
|
||||
{
|
||||
outb(reg, CSC_INDEX);
|
||||
outb(data, CSC_DATA);
|
||||
}
|
||||
|
||||
static inline unsigned char getcsc(int reg)
|
||||
{
|
||||
outb(reg, CSC_INDEX);
|
||||
return(inb(CSC_DATA));
|
||||
}
|
||||
|
||||
static inline void setpcc(int reg, unsigned char data)
|
||||
{
|
||||
outb(reg, PCC_INDEX);
|
||||
outb(data, PCC_DATA);
|
||||
}
|
||||
|
||||
static inline unsigned char getpcc(int reg)
|
||||
{
|
||||
outb(reg, PCC_INDEX);
|
||||
return(inb(PCC_DATA));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
************************************************************
|
||||
** Enable access to DIL/NetPC's flash by mapping it into
|
||||
** the SC4x0's MMS Window C.
|
||||
************************************************************
|
||||
*/
|
||||
static void dnpc_map_flash(unsigned long flash_base, unsigned long flash_size)
|
||||
{
|
||||
unsigned long flash_end = flash_base + flash_size - 1;
|
||||
|
||||
/*
|
||||
** enable setup of MMS windows C-F:
|
||||
*/
|
||||
/* - enable PC Card indexed register space */
|
||||
setcsc(CSC_CR, getcsc(CSC_CR) | 0x2);
|
||||
/* - set PC Card controller to operate in standard mode */
|
||||
setcsc(CSC_PCCMDCR, getcsc(CSC_PCCMDCR) & ~1);
|
||||
|
||||
/*
|
||||
** Program base address and end address of window
|
||||
** where the flash ROM should appear in CPU address space
|
||||
*/
|
||||
setpcc(PCC_MWSAR_1_Lo, (flash_base >> 12) & 0xff);
|
||||
setpcc(PCC_MWSAR_1_Hi, (flash_base >> 20) & 0x3f);
|
||||
setpcc(PCC_MWEAR_1_Lo, (flash_end >> 12) & 0xff);
|
||||
setpcc(PCC_MWEAR_1_Hi, (flash_end >> 20) & 0x3f);
|
||||
|
||||
/* program offset of first flash location to appear in this window (0) */
|
||||
setpcc(PCC_MWAOR_1_Lo, ((0 - flash_base) >> 12) & 0xff);
|
||||
setpcc(PCC_MWAOR_1_Hi, ((0 - flash_base)>> 20) & 0x3f);
|
||||
|
||||
/* set attributes for MMS window C: non-cacheable, write-enabled */
|
||||
setcsc(CSC_MMSWAR, getcsc(CSC_MMSWAR) & ~0x11);
|
||||
|
||||
/* select physical device ROMCS0 (i.e. flash) for MMS Window C */
|
||||
setcsc(CSC_MMSWDSR, getcsc(CSC_MMSWDSR) & ~0x03);
|
||||
|
||||
/* enable memory window 1 */
|
||||
setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) | 0x02);
|
||||
|
||||
/* now disable PC Card indexed register space again */
|
||||
setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
************************************************************
|
||||
** Disable access to DIL/NetPC's flash by mapping it into
|
||||
** the SC4x0's MMS Window C.
|
||||
************************************************************
|
||||
*/
|
||||
static void dnpc_unmap_flash(void)
|
||||
{
|
||||
/* - enable PC Card indexed register space */
|
||||
setcsc(CSC_CR, getcsc(CSC_CR) | 0x2);
|
||||
|
||||
/* disable memory window 1 */
|
||||
setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) & ~0x02);
|
||||
|
||||
/* now disable PC Card indexed register space again */
|
||||
setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
************************************************************
|
||||
** Enable/Disable VPP to write to flash
|
||||
************************************************************
|
||||
*/
|
||||
|
||||
static DEFINE_SPINLOCK(dnpc_spin);
|
||||
static int vpp_counter = 0;
|
||||
/*
|
||||
** This is what has to be done for the DNP board ..
|
||||
*/
|
||||
static void dnp_set_vpp(struct map_info *not_used, int on)
|
||||
{
|
||||
spin_lock_irq(&dnpc_spin);
|
||||
|
||||
if (on)
|
||||
{
|
||||
if(++vpp_counter == 1)
|
||||
setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x4);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(--vpp_counter == 0)
|
||||
setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x4);
|
||||
else
|
||||
BUG_ON(vpp_counter < 0);
|
||||
}
|
||||
spin_unlock_irq(&dnpc_spin);
|
||||
}
|
||||
|
||||
/*
|
||||
** .. and this the ADNP version:
|
||||
*/
|
||||
static void adnp_set_vpp(struct map_info *not_used, int on)
|
||||
{
|
||||
spin_lock_irq(&dnpc_spin);
|
||||
|
||||
if (on)
|
||||
{
|
||||
if(++vpp_counter == 1)
|
||||
setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x8);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(--vpp_counter == 0)
|
||||
setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x8);
|
||||
else
|
||||
BUG_ON(vpp_counter < 0);
|
||||
}
|
||||
spin_unlock_irq(&dnpc_spin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define DNP_WINDOW_SIZE 0x00200000 /* DNP flash size is 2MiB */
|
||||
#define ADNP_WINDOW_SIZE 0x00400000 /* ADNP flash size is 4MiB */
|
||||
#define WINDOW_ADDR FLASH_BASE
|
||||
|
||||
static struct map_info dnpc_map = {
|
||||
.name = "ADNP Flash Bank",
|
||||
.size = ADNP_WINDOW_SIZE,
|
||||
.bankwidth = 1,
|
||||
.set_vpp = adnp_set_vpp,
|
||||
.phys = WINDOW_ADDR
|
||||
};
|
||||
|
||||
/*
|
||||
** The layout of the flash is somewhat "strange":
|
||||
**
|
||||
** 1. 960 KiB (15 blocks) : Space for ROM Bootloader and user data
|
||||
** 2. 64 KiB (1 block) : System BIOS
|
||||
** 3. 960 KiB (15 blocks) : User Data (DNP model) or
|
||||
** 3. 3008 KiB (47 blocks) : User Data (ADNP model)
|
||||
** 4. 64 KiB (1 block) : System BIOS Entry
|
||||
*/
|
||||
|
||||
static struct mtd_partition partition_info[]=
|
||||
{
|
||||
{
|
||||
.name = "ADNP boot",
|
||||
.offset = 0,
|
||||
.size = 0xf0000,
|
||||
},
|
||||
{
|
||||
.name = "ADNP system BIOS",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = 0x10000,
|
||||
#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
#endif
|
||||
},
|
||||
{
|
||||
.name = "ADNP file system",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = 0x2f0000,
|
||||
},
|
||||
{
|
||||
.name = "ADNP system BIOS entry",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
static struct mtd_info *lowlvl_parts[NUM_PARTITIONS];
|
||||
static struct mtd_info *merged_mtd;
|
||||
|
||||
/*
|
||||
** "Highlevel" partition info:
|
||||
**
|
||||
** Using the MTD concat layer, we can re-arrange partitions to our
|
||||
** liking: we construct a virtual MTD device by concatenating the
|
||||
** partitions, specifying the sequence such that the boot block
|
||||
** is immediately followed by the filesystem block (i.e. the stupid
|
||||
** system BIOS block is mapped to a different place). When re-partitioning
|
||||
** this concatenated MTD device, we can set the boot block size to
|
||||
** an arbitrary (though erase block aligned) value i.e. not one that
|
||||
** is dictated by the flash's physical layout. We can thus set the
|
||||
** boot block to be e.g. 64 KB (which is fully sufficient if we want
|
||||
** to boot an etherboot image) or to -say- 1.5 MB if we want to boot
|
||||
** a large kernel image. In all cases, the remainder of the flash
|
||||
** is available as file system space.
|
||||
*/
|
||||
|
||||
static struct mtd_partition higlvl_partition_info[]=
|
||||
{
|
||||
{
|
||||
.name = "ADNP boot block",
|
||||
.offset = 0,
|
||||
.size = CONFIG_MTD_DILNETPC_BOOTSIZE,
|
||||
},
|
||||
{
|
||||
.name = "ADNP file system space",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
|
||||
},
|
||||
{
|
||||
.name = "ADNP system BIOS + BIOS Entry",
|
||||
.offset = MTDPART_OFS_NXTBLK,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
|
||||
.mask_flags = MTD_WRITEABLE,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
#define NUM_HIGHLVL_PARTITIONS ARRAY_SIZE(higlvl_partition_info)
|
||||
|
||||
|
||||
static int dnp_adnp_probe(void)
|
||||
{
|
||||
char *biosid, rc = -1;
|
||||
|
||||
biosid = (char*)ioremap(BIOSID_BASE, 16);
|
||||
if(biosid)
|
||||
{
|
||||
if(!strcmp(biosid, ID_DNPC))
|
||||
rc = 1; /* this is a DNPC */
|
||||
else if(!strcmp(biosid, ID_ADNP))
|
||||
rc = 0; /* this is a ADNPC */
|
||||
}
|
||||
iounmap((void *)biosid);
|
||||
return(rc);
|
||||
}
|
||||
|
||||
|
||||
static int __init init_dnpc(void)
|
||||
{
|
||||
int is_dnp;
|
||||
|
||||
/*
|
||||
** determine hardware (DNP/ADNP/invalid)
|
||||
*/
|
||||
if((is_dnp = dnp_adnp_probe()) < 0)
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
** Things are set up for ADNP by default
|
||||
** -> modify all that needs to be different for DNP
|
||||
*/
|
||||
if(is_dnp)
|
||||
{ /*
|
||||
** Adjust window size, select correct set_vpp function.
|
||||
** The partitioning scheme is identical on both DNP
|
||||
** and ADNP except for the size of the third partition.
|
||||
*/
|
||||
int i;
|
||||
dnpc_map.size = DNP_WINDOW_SIZE;
|
||||
dnpc_map.set_vpp = dnp_set_vpp;
|
||||
partition_info[2].size = 0xf0000;
|
||||
|
||||
/*
|
||||
** increment all string pointers so the leading 'A' gets skipped,
|
||||
** thus turning all occurrences of "ADNP ..." into "DNP ..."
|
||||
*/
|
||||
++dnpc_map.name;
|
||||
for(i = 0; i < NUM_PARTITIONS; i++)
|
||||
++partition_info[i].name;
|
||||
higlvl_partition_info[1].size = DNP_WINDOW_SIZE -
|
||||
CONFIG_MTD_DILNETPC_BOOTSIZE - 0x20000;
|
||||
for(i = 0; i < NUM_HIGHLVL_PARTITIONS; i++)
|
||||
++higlvl_partition_info[i].name;
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%llx\n",
|
||||
is_dnp ? "DNPC" : "ADNP", dnpc_map.size, (unsigned long long)dnpc_map.phys);
|
||||
|
||||
dnpc_map.virt = ioremap_nocache(dnpc_map.phys, dnpc_map.size);
|
||||
|
||||
dnpc_map_flash(dnpc_map.phys, dnpc_map.size);
|
||||
|
||||
if (!dnpc_map.virt) {
|
||||
printk("Failed to ioremap_nocache\n");
|
||||
return -EIO;
|
||||
}
|
||||
simple_map_init(&dnpc_map);
|
||||
|
||||
printk("FLASH virtual address: 0x%p\n", dnpc_map.virt);
|
||||
|
||||
mymtd = do_map_probe("jedec_probe", &dnpc_map);
|
||||
|
||||
if (!mymtd)
|
||||
mymtd = do_map_probe("cfi_probe", &dnpc_map);
|
||||
|
||||
/*
|
||||
** If flash probes fail, try to make flashes accessible
|
||||
** at least as ROM. Ajust erasesize in this case since
|
||||
** the default one (128M) will break our partitioning
|
||||
*/
|
||||
if (!mymtd)
|
||||
if((mymtd = do_map_probe("map_rom", &dnpc_map)))
|
||||
mymtd->erasesize = 0x10000;
|
||||
|
||||
if (!mymtd) {
|
||||
iounmap(dnpc_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mymtd->owner = THIS_MODULE;
|
||||
|
||||
/*
|
||||
** Supply pointers to lowlvl_parts[] array to add_mtd_partitions()
|
||||
** -> add_mtd_partitions() will _not_ register MTD devices for
|
||||
** the partitions, but will instead store pointers to the MTD
|
||||
** objects it creates into our lowlvl_parts[] array.
|
||||
** NOTE: we arrange the pointers such that the sequence of the
|
||||
** partitions gets re-arranged: partition #2 follows
|
||||
** partition #0.
|
||||
*/
|
||||
partition_info[0].mtdp = &lowlvl_parts[0];
|
||||
partition_info[1].mtdp = &lowlvl_parts[2];
|
||||
partition_info[2].mtdp = &lowlvl_parts[1];
|
||||
partition_info[3].mtdp = &lowlvl_parts[3];
|
||||
|
||||
mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
|
||||
|
||||
/*
|
||||
** now create a virtual MTD device by concatenating the for partitions
|
||||
** (in the sequence given by the lowlvl_parts[] array.
|
||||
*/
|
||||
merged_mtd = mtd_concat_create(lowlvl_parts, NUM_PARTITIONS, "(A)DNP Flash Concatenated");
|
||||
if(merged_mtd)
|
||||
{ /*
|
||||
** now partition the new device the way we want it. This time,
|
||||
** we do not supply mtd pointers in higlvl_partition_info, so
|
||||
** add_mtd_partitions() will register the devices.
|
||||
*/
|
||||
mtd_device_register(merged_mtd, higlvl_partition_info,
|
||||
NUM_HIGHLVL_PARTITIONS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_dnpc(void)
|
||||
{
|
||||
if(merged_mtd) {
|
||||
mtd_device_unregister(merged_mtd);
|
||||
mtd_concat_destroy(merged_mtd);
|
||||
}
|
||||
|
||||
if (mymtd) {
|
||||
mtd_device_unregister(mymtd);
|
||||
map_destroy(mymtd);
|
||||
}
|
||||
if (dnpc_map.virt) {
|
||||
iounmap(dnpc_map.virt);
|
||||
dnpc_unmap_flash();
|
||||
dnpc_map.virt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_dnpc);
|
||||
module_exit(cleanup_dnpc);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
|
||||
MODULE_DESCRIPTION("MTD map driver for SSV DIL/NetPC DNP & ADNP");
|
|
@ -1,146 +0,0 @@
|
|||
|
||||
/*
|
||||
* drivers/mtd/maps/dmv182.c
|
||||
*
|
||||
* Flash map driver for the Dy4 SVME182 board
|
||||
*
|
||||
* Copyright 2003-2004, TimeSys Corporation
|
||||
*
|
||||
* Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
/*
|
||||
* This driver currently handles only the 16MiB user flash bank 1 on the
|
||||
* board. It does not provide access to bank 0 (contains the Dy4 FFW), bank 2
|
||||
* (VxWorks boot), or the optional 48MiB expansion flash.
|
||||
*
|
||||
* scott.wood@timesys.com: On the newer boards with 128MiB flash, it
|
||||
* now supports the first 96MiB (the boot flash bank containing FFW
|
||||
* is excluded). The VxWorks loader is in partition 1.
|
||||
*/
|
||||
|
||||
#define FLASH_BASE_ADDR 0xf0000000
|
||||
#define FLASH_BANK_SIZE (128*1024*1024)
|
||||
|
||||
MODULE_AUTHOR("Scott Wood, TimeSys Corporation <scott.wood@timesys.com>");
|
||||
MODULE_DESCRIPTION("User-programmable flash device on the Dy4 SVME182 board");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static struct map_info svme182_map = {
|
||||
.name = "Dy4 SVME182",
|
||||
.bankwidth = 32,
|
||||
.size = 128 * 1024 * 1024
|
||||
};
|
||||
|
||||
#define BOOTIMAGE_PART_SIZE ((6*1024*1024)-RESERVED_PART_SIZE)
|
||||
|
||||
// Allow 6MiB for the kernel
|
||||
#define NEW_BOOTIMAGE_PART_SIZE (6 * 1024 * 1024)
|
||||
// Allow 1MiB for the bootloader
|
||||
#define NEW_BOOTLOADER_PART_SIZE (1024 * 1024)
|
||||
// Use the remaining 9MiB at the end of flash for the RFS
|
||||
#define NEW_RFS_PART_SIZE (0x01000000 - NEW_BOOTLOADER_PART_SIZE - \
|
||||
NEW_BOOTIMAGE_PART_SIZE)
|
||||
|
||||
static struct mtd_partition svme182_partitions[] = {
|
||||
// The Lower PABS is only 128KiB, but the partition code doesn't
|
||||
// like partitions that don't end on the largest erase block
|
||||
// size of the device, even if all of the erase blocks in the
|
||||
// partition are small ones. The hardware should prevent
|
||||
// writes to the actual PABS areas.
|
||||
{
|
||||
name: "Lower PABS and CPU 0 bootloader or kernel",
|
||||
size: 6*1024*1024,
|
||||
offset: 0,
|
||||
},
|
||||
{
|
||||
name: "Root Filesystem",
|
||||
size: 10*1024*1024,
|
||||
offset: MTDPART_OFS_NXTBLK
|
||||
},
|
||||
{
|
||||
name: "CPU1 Bootloader",
|
||||
size: 1024*1024,
|
||||
offset: MTDPART_OFS_NXTBLK,
|
||||
},
|
||||
{
|
||||
name: "Extra",
|
||||
size: 110*1024*1024,
|
||||
offset: MTDPART_OFS_NXTBLK
|
||||
},
|
||||
{
|
||||
name: "Foundation Firmware and Upper PABS",
|
||||
size: 1024*1024,
|
||||
offset: MTDPART_OFS_NXTBLK,
|
||||
mask_flags: MTD_WRITEABLE // read-only
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_info *this_mtd;
|
||||
|
||||
static int __init init_svme182(void)
|
||||
{
|
||||
struct mtd_partition *partitions;
|
||||
int num_parts = ARRAY_SIZE(svme182_partitions);
|
||||
|
||||
partitions = svme182_partitions;
|
||||
|
||||
svme182_map.virt = ioremap(FLASH_BASE_ADDR, svme182_map.size);
|
||||
|
||||
if (svme182_map.virt == 0) {
|
||||
printk("Failed to ioremap FLASH memory area.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
simple_map_init(&svme182_map);
|
||||
|
||||
this_mtd = do_map_probe("cfi_probe", &svme182_map);
|
||||
if (!this_mtd)
|
||||
{
|
||||
iounmap((void *)svme182_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE "SVME182 flash device: %dMiB at 0x%08x\n",
|
||||
this_mtd->size >> 20, FLASH_BASE_ADDR);
|
||||
|
||||
this_mtd->owner = THIS_MODULE;
|
||||
mtd_device_register(this_mtd, partitions, num_parts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_svme182(void)
|
||||
{
|
||||
if (this_mtd)
|
||||
{
|
||||
mtd_device_unregister(this_mtd);
|
||||
map_destroy(this_mtd);
|
||||
}
|
||||
|
||||
if (svme182_map.virt)
|
||||
{
|
||||
iounmap((void *)svme182_map.virt);
|
||||
svme182_map.virt = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
module_init(init_svme182);
|
||||
module_exit(cleanup_svme182);
|
|
@ -157,7 +157,8 @@ static void gf_copy_to(struct map_info *map, unsigned long to,
|
|||
memcpy_toio(map->virt + (to % state->win_size), from, len);
|
||||
}
|
||||
|
||||
static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
|
||||
static const char * const part_probe_types[] = {
|
||||
"cmdlinepart", "RedBoot", NULL };
|
||||
|
||||
/**
|
||||
* gpio_flash_probe() - setup a mapping for a GPIO assisted flash
|
||||
|
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Flash memory access on Hynix GMS30C7201/HMS30C7202 based
|
||||
* evaluation boards
|
||||
*
|
||||
* (C) 2002 Jungjun Kim <jungjun.kim@hynix.com>
|
||||
* 2003 Thomas Gleixner <tglx@linutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
|
||||
static struct map_info h720x_map = {
|
||||
.name = "H720X",
|
||||
.bankwidth = 4,
|
||||
.size = H720X_FLASH_SIZE,
|
||||
.phys = H720X_FLASH_PHYS,
|
||||
};
|
||||
|
||||
static struct mtd_partition h720x_partitions[] = {
|
||||
{
|
||||
.name = "ArMon",
|
||||
.size = 0x00080000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "Env",
|
||||
.size = 0x00040000,
|
||||
.offset = 0x00080000,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "Kernel",
|
||||
.size = 0x00180000,
|
||||
.offset = 0x000c0000,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "Ramdisk",
|
||||
.size = 0x00400000,
|
||||
.offset = 0x00240000,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "jffs2",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = MTDPART_OFS_APPEND
|
||||
}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS ARRAY_SIZE(h720x_partitions)
|
||||
|
||||
/*
|
||||
* Initialize FLASH support
|
||||
*/
|
||||
static int __init h720x_mtd_init(void)
|
||||
{
|
||||
h720x_map.virt = ioremap(h720x_map.phys, h720x_map.size);
|
||||
|
||||
if (!h720x_map.virt) {
|
||||
printk(KERN_ERR "H720x-MTD: ioremap failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
simple_map_init(&h720x_map);
|
||||
|
||||
// Probe for flash bankwidth 4
|
||||
printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n");
|
||||
mymtd = do_map_probe("cfi_probe", &h720x_map);
|
||||
if (!mymtd) {
|
||||
printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n");
|
||||
// Probe for bankwidth 2
|
||||
h720x_map.bankwidth = 2;
|
||||
mymtd = do_map_probe("cfi_probe", &h720x_map);
|
||||
}
|
||||
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
|
||||
mtd_device_parse_register(mymtd, NULL, NULL,
|
||||
h720x_partitions, NUM_PARTITIONS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap((void *)h720x_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup
|
||||
*/
|
||||
static void __exit h720x_mtd_cleanup(void)
|
||||
{
|
||||
|
||||
if (mymtd) {
|
||||
mtd_device_unregister(mymtd);
|
||||
map_destroy(mymtd);
|
||||
}
|
||||
|
||||
if (h720x_map.virt) {
|
||||
iounmap((void *)h720x_map.virt);
|
||||
h720x_map.virt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module_init(h720x_mtd_init);
|
||||
module_exit(h720x_mtd_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards");
|
|
@ -24,14 +24,12 @@
|
|||
#define NUM_FLASHBANKS 2
|
||||
#define BUSWIDTH 4
|
||||
|
||||
/* can be { "cfi_probe", "jedec_probe", "map_rom", NULL } */
|
||||
#define PROBETYPES { "jedec_probe", NULL }
|
||||
|
||||
#define MSG_PREFIX "impA7:" /* prefix for our printk()'s */
|
||||
#define MTDID "impa7-%d" /* for mtdparts= partitioning */
|
||||
|
||||
static struct mtd_info *impa7_mtd[NUM_FLASHBANKS];
|
||||
|
||||
static const char * const rom_probe_types[] = { "jedec_probe", NULL };
|
||||
|
||||
static struct map_info impa7_map[NUM_FLASHBANKS] = {
|
||||
{
|
||||
|
@ -60,8 +58,7 @@ static struct mtd_partition partitions[] =
|
|||
|
||||
static int __init init_impa7(void)
|
||||
{
|
||||
static const char *rom_probe_types[] = PROBETYPES;
|
||||
const char **type;
|
||||
const char * const *type;
|
||||
int i;
|
||||
static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = {
|
||||
{ WINDOW_ADDR0, WINDOW_SIZE0 },
|
||||
|
|
|
@ -82,9 +82,9 @@ static void vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
|
|||
|
||||
static int vr_nor_mtd_setup(struct vr_nor_mtd *p)
|
||||
{
|
||||
static const char *probe_types[] =
|
||||
static const char * const probe_types[] =
|
||||
{ "cfi_probe", "jedec_probe", NULL };
|
||||
const char **type;
|
||||
const char * const *type;
|
||||
|
||||
for (type = probe_types; !p->info && *type; type++)
|
||||
p->info = do_map_probe(*type, &p->map);
|
||||
|
|
|
@ -1,253 +0,0 @@
|
|||
/*
|
||||
* drivers/mtd/maps/ixp2000.c
|
||||
*
|
||||
* Mapping for the Intel XScale IXP2000 based systems
|
||||
*
|
||||
* Copyright (C) 2002 Intel Corp.
|
||||
* Copyright (C) 2003-2004 MontaVista Software, Inc.
|
||||
*
|
||||
* Original Author: Naeem M Afzal <naeem.m.afzal@intel.com>
|
||||
* Maintainer: Deepak Saxena <dsaxena@plexity.net>
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ioport.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/io.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/mach/flash.h>
|
||||
|
||||
#include <linux/reboot.h>
|
||||
|
||||
struct ixp2000_flash_info {
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct resource *res;
|
||||
};
|
||||
|
||||
static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
unsigned long (*set_bank)(unsigned long) =
|
||||
(unsigned long(*)(unsigned long))map->map_priv_2;
|
||||
|
||||
return (set_bank ? set_bank(ofs) : ofs);
|
||||
}
|
||||
|
||||
#ifdef __ARMEB__
|
||||
/*
|
||||
* Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which
|
||||
* causes the lower address bits to be XORed with 0x11 on 8 bit accesses
|
||||
* and XORed with 0x10 on 16 bit accesses. See the spec update, erratum 44.
|
||||
*/
|
||||
static int erratum44_workaround = 0;
|
||||
|
||||
static inline unsigned long address_fix8_write(unsigned long addr)
|
||||
{
|
||||
if (erratum44_workaround) {
|
||||
return (addr ^ 3);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
#else
|
||||
|
||||
#define address_fix8_write(x) (x)
|
||||
#endif
|
||||
|
||||
static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
map_word val;
|
||||
|
||||
val.x[0] = *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs)));
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't use the standard memcpy due to the broken SlowPort
|
||||
* address translation on rev A0 and A1 silicon and the fact that
|
||||
* we have banked flash.
|
||||
*/
|
||||
static void ixp2000_flash_copy_from(struct map_info *map, void *to,
|
||||
unsigned long from, ssize_t len)
|
||||
{
|
||||
from = flash_bank_setup(map, from);
|
||||
while(len--)
|
||||
*(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++);
|
||||
}
|
||||
|
||||
static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs)
|
||||
{
|
||||
*(__u8 *) (address_fix8_write(map->map_priv_1 +
|
||||
flash_bank_setup(map, ofs))) = d.x[0];
|
||||
}
|
||||
|
||||
static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to,
|
||||
const void *from, ssize_t len)
|
||||
{
|
||||
to = flash_bank_setup(map, to);
|
||||
while(len--) {
|
||||
unsigned long tmp = address_fix8_write(map->map_priv_1 + to++);
|
||||
*(__u8 *)(tmp) = *(__u8 *)(from++);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int ixp2000_flash_remove(struct platform_device *dev)
|
||||
{
|
||||
struct flash_platform_data *plat = dev->dev.platform_data;
|
||||
struct ixp2000_flash_info *info = platform_get_drvdata(dev);
|
||||
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
if(!info)
|
||||
return 0;
|
||||
|
||||
if (info->mtd) {
|
||||
mtd_device_unregister(info->mtd);
|
||||
map_destroy(info->mtd);
|
||||
}
|
||||
if (info->map.map_priv_1)
|
||||
iounmap((void *) info->map.map_priv_1);
|
||||
|
||||
if (info->res) {
|
||||
release_resource(info->res);
|
||||
kfree(info->res);
|
||||
}
|
||||
|
||||
if (plat->exit)
|
||||
plat->exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ixp2000_flash_probe(struct platform_device *dev)
|
||||
{
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
struct ixp2000_flash_data *ixp_data = dev->dev.platform_data;
|
||||
struct flash_platform_data *plat;
|
||||
struct ixp2000_flash_info *info;
|
||||
unsigned long window_size;
|
||||
int err = -1;
|
||||
|
||||
if (!ixp_data)
|
||||
return -ENODEV;
|
||||
|
||||
plat = ixp_data->platform_data;
|
||||
if (!plat)
|
||||
return -ENODEV;
|
||||
|
||||
window_size = resource_size(dev->resource);
|
||||
dev_info(&dev->dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n",
|
||||
ixp_data->nr_banks, ((u32)window_size >> 20));
|
||||
|
||||
if (plat->width != 1) {
|
||||
dev_err(&dev->dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n",
|
||||
plat->width * 8);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
info = kzalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL);
|
||||
if(!info) {
|
||||
err = -ENOMEM;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(dev, info);
|
||||
|
||||
/*
|
||||
* Tell the MTD layer we're not 1:1 mapped so that it does
|
||||
* not attempt to do a direct access on us.
|
||||
*/
|
||||
info->map.phys = NO_XIP;
|
||||
|
||||
info->map.size = ixp_data->nr_banks * window_size;
|
||||
info->map.bankwidth = 1;
|
||||
|
||||
/*
|
||||
* map_priv_2 is used to store a ptr to the bank_setup routine
|
||||
*/
|
||||
info->map.map_priv_2 = (unsigned long) ixp_data->bank_setup;
|
||||
|
||||
info->map.name = dev_name(&dev->dev);
|
||||
info->map.read = ixp2000_flash_read8;
|
||||
info->map.write = ixp2000_flash_write8;
|
||||
info->map.copy_from = ixp2000_flash_copy_from;
|
||||
info->map.copy_to = ixp2000_flash_copy_to;
|
||||
|
||||
info->res = request_mem_region(dev->resource->start,
|
||||
resource_size(dev->resource),
|
||||
dev_name(&dev->dev));
|
||||
if (!info->res) {
|
||||
dev_err(&dev->dev, "Could not reserve memory region\n");
|
||||
err = -ENOMEM;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
info->map.map_priv_1 =
|
||||
(unsigned long)ioremap(dev->resource->start,
|
||||
resource_size(dev->resource));
|
||||
if (!info->map.map_priv_1) {
|
||||
dev_err(&dev->dev, "Failed to ioremap flash region\n");
|
||||
err = -EIO;
|
||||
goto Error;
|
||||
}
|
||||
|
||||
#if defined(__ARMEB__)
|
||||
/*
|
||||
* Enable erratum 44 workaround for NPUs with broken slowport
|
||||
*/
|
||||
|
||||
erratum44_workaround = ixp2000_has_broken_slowport();
|
||||
dev_info(&dev->dev, "Erratum 44 workaround %s\n",
|
||||
erratum44_workaround ? "enabled" : "disabled");
|
||||
#endif
|
||||
|
||||
info->mtd = do_map_probe(plat->map_name, &info->map);
|
||||
if (!info->mtd) {
|
||||
dev_err(&dev->dev, "map_probe failed\n");
|
||||
err = -ENXIO;
|
||||
goto Error;
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
err = mtd_device_parse_register(info->mtd, probes, NULL, NULL, 0);
|
||||
if (err)
|
||||
goto Error;
|
||||
|
||||
return 0;
|
||||
|
||||
Error:
|
||||
ixp2000_flash_remove(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver ixp2000_flash_driver = {
|
||||
.probe = ixp2000_flash_probe,
|
||||
.remove = ixp2000_flash_remove,
|
||||
.driver = {
|
||||
.name = "IXP2000-Flash",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ixp2000_flash_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
|
||||
MODULE_ALIAS("platform:IXP2000-Flash");
|
|
@ -148,7 +148,7 @@ struct ixp4xx_flash_info {
|
|||
struct resource *res;
|
||||
};
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int ixp4xx_flash_remove(struct platform_device *dev)
|
||||
{
|
||||
|
|
|
@ -46,8 +46,7 @@ struct ltq_mtd {
|
|||
};
|
||||
|
||||
static const char ltq_map_name[] = "ltq_nor";
|
||||
static const char *ltq_probe_types[] = {
|
||||
"cmdlinepart", "ofpart", NULL };
|
||||
static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL };
|
||||
|
||||
static map_word
|
||||
ltq_read16(struct map_info *map, unsigned long adr)
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Handle mapping of the flash on MBX860 boards
|
||||
*
|
||||
* Author: Anton Todorov
|
||||
* Copyright: (C) 2001 Emness Technology
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
|
||||
#define WINDOW_ADDR 0xfe000000
|
||||
#define WINDOW_SIZE 0x00200000
|
||||
|
||||
/* Flash / Partition sizing */
|
||||
#define MAX_SIZE_KiB 8192
|
||||
#define BOOT_PARTITION_SIZE_KiB 512
|
||||
#define KERNEL_PARTITION_SIZE_KiB 5632
|
||||
#define APP_PARTITION_SIZE_KiB 2048
|
||||
|
||||
#define NUM_PARTITIONS 3
|
||||
|
||||
/* partition_info gives details on the logical partitions that the split the
|
||||
* single flash device into. If the size if zero we use up to the end of the
|
||||
* device. */
|
||||
static struct mtd_partition partition_info[]={
|
||||
{ .name = "MBX flash BOOT partition",
|
||||
.offset = 0,
|
||||
.size = BOOT_PARTITION_SIZE_KiB*1024 },
|
||||
{ .name = "MBX flash DATA partition",
|
||||
.offset = BOOT_PARTITION_SIZE_KiB*1024,
|
||||
.size = (KERNEL_PARTITION_SIZE_KiB)*1024 },
|
||||
{ .name = "MBX flash APPLICATION partition",
|
||||
.offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
|
||||
};
|
||||
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
|
||||
struct map_info mbx_map = {
|
||||
.name = "MBX flash",
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = WINDOW_ADDR,
|
||||
.bankwidth = 4,
|
||||
};
|
||||
|
||||
static int __init init_mbx(void)
|
||||
{
|
||||
printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR);
|
||||
mbx_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
|
||||
|
||||
if (!mbx_map.virt) {
|
||||
printk("Failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
simple_map_init(&mbx_map);
|
||||
|
||||
mymtd = do_map_probe("jedec_probe", &mbx_map);
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
mtd_device_register(mymtd, NULL, 0);
|
||||
mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap((void *)mbx_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mbx(void)
|
||||
{
|
||||
if (mymtd) {
|
||||
mtd_device_unregister(mymtd);
|
||||
map_destroy(mymtd);
|
||||
}
|
||||
if (mbx_map.virt) {
|
||||
iounmap((void *)mbx_map.virt);
|
||||
mbx_map.virt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_mbx);
|
||||
module_exit(cleanup_mbx);
|
||||
|
||||
MODULE_AUTHOR("Anton Todorov <a.todorov@emness.com>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Motorola MBX860 board");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -283,8 +283,7 @@ static int mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||
if (err)
|
||||
goto release;
|
||||
|
||||
/* tsk - do_map_probe should take const char * */
|
||||
mtd = do_map_probe((char *)info->map_name, &map->map);
|
||||
mtd = do_map_probe(info->map_name, &map->map);
|
||||
err = -ENODEV;
|
||||
if (!mtd)
|
||||
goto release;
|
||||
|
|
|
@ -87,21 +87,18 @@ static void physmap_set_vpp(struct map_info *map, int state)
|
|||
spin_unlock_irqrestore(&info->vpp_lock, flags);
|
||||
}
|
||||
|
||||
static const char *rom_probe_types[] = {
|
||||
"cfi_probe",
|
||||
"jedec_probe",
|
||||
"qinfo_probe",
|
||||
"map_rom",
|
||||
NULL };
|
||||
static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", "afs",
|
||||
NULL };
|
||||
static const char * const rom_probe_types[] = {
|
||||
"cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", NULL };
|
||||
|
||||
static const char * const part_probe_types[] = {
|
||||
"cmdlinepart", "RedBoot", "afs", NULL };
|
||||
|
||||
static int physmap_flash_probe(struct platform_device *dev)
|
||||
{
|
||||
struct physmap_flash_data *physmap_data;
|
||||
struct physmap_flash_info *info;
|
||||
const char **probe_type;
|
||||
const char **part_types;
|
||||
const char * const *probe_type;
|
||||
const char * const *part_types;
|
||||
int err = 0;
|
||||
int i;
|
||||
int devices_found = 0;
|
||||
|
|
|
@ -71,6 +71,9 @@ static int of_flash_remove(struct platform_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char * const rom_probe_types[] = {
|
||||
"cfi_probe", "jedec_probe", "map_rom" };
|
||||
|
||||
/* Helper function to handle probing of the obsolete "direct-mapped"
|
||||
* compatible binding, which has an extra "probe-type" property
|
||||
* describing the type of flash probe necessary. */
|
||||
|
@ -80,8 +83,6 @@ static struct mtd_info *obsolete_probe(struct platform_device *dev,
|
|||
struct device_node *dp = dev->dev.of_node;
|
||||
const char *of_probe;
|
||||
struct mtd_info *mtd;
|
||||
static const char *rom_probe_types[]
|
||||
= { "cfi_probe", "jedec_probe", "map_rom"};
|
||||
int i;
|
||||
|
||||
dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
|
||||
|
@ -111,9 +112,10 @@ static struct mtd_info *obsolete_probe(struct platform_device *dev,
|
|||
specifies the list of partition probers to use. If none is given then the
|
||||
default is use. These take precedence over other device tree
|
||||
information. */
|
||||
static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot",
|
||||
"ofpart", "ofoldpart", NULL };
|
||||
static const char **of_get_probes(struct device_node *dp)
|
||||
static const char * const part_probe_types_def[] = {
|
||||
"cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL };
|
||||
|
||||
static const char * const *of_get_probes(struct device_node *dp)
|
||||
{
|
||||
const char *cp;
|
||||
int cplen;
|
||||
|
@ -142,7 +144,7 @@ static const char **of_get_probes(struct device_node *dp)
|
|||
return res;
|
||||
}
|
||||
|
||||
static void of_free_probes(const char **probes)
|
||||
static void of_free_probes(const char * const *probes)
|
||||
{
|
||||
if (probes != part_probe_types_def)
|
||||
kfree(probes);
|
||||
|
@ -151,7 +153,7 @@ static void of_free_probes(const char **probes)
|
|||
static struct of_device_id of_flash_match[];
|
||||
static int of_flash_probe(struct platform_device *dev)
|
||||
{
|
||||
const char **part_probe_types;
|
||||
const char * const *part_probe_types;
|
||||
const struct of_device_id *match;
|
||||
struct device_node *dp = dev->dev.of_node;
|
||||
struct resource res;
|
||||
|
|
|
@ -199,7 +199,7 @@ static int platram_probe(struct platform_device *pdev)
|
|||
* supplied by the platform_data struct */
|
||||
|
||||
if (pdata->map_probes) {
|
||||
const char **map_probes = pdata->map_probes;
|
||||
const char * const *map_probes = pdata->map_probes;
|
||||
|
||||
for ( ; !info->mtd && *map_probes; map_probes++)
|
||||
info->mtd = do_map_probe(*map_probes , &info->map);
|
||||
|
|
|
@ -45,9 +45,7 @@ struct pxa2xx_flash_info {
|
|||
struct map_info map;
|
||||
};
|
||||
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int pxa2xx_flash_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
|
|
@ -45,14 +45,15 @@ static int rbtx4939_flash_remove(struct platform_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
|
||||
static const char * const rom_probe_types[] = {
|
||||
"cfi_probe", "jedec_probe", NULL };
|
||||
|
||||
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;
|
||||
const char * const *probe_type;
|
||||
int err = 0;
|
||||
unsigned long size;
|
||||
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Handle mapping of the flash on the RPX Lite and CLLF boards
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
||||
|
||||
#define WINDOW_ADDR 0xfe000000
|
||||
#define WINDOW_SIZE 0x800000
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
|
||||
static struct map_info rpxlite_map = {
|
||||
.name = "RPX",
|
||||
.size = WINDOW_SIZE,
|
||||
.bankwidth = 4,
|
||||
.phys = WINDOW_ADDR,
|
||||
};
|
||||
|
||||
static int __init init_rpxlite(void)
|
||||
{
|
||||
printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
|
||||
rpxlite_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
|
||||
|
||||
if (!rpxlite_map.virt) {
|
||||
printk("Failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
simple_map_init(&rpxlite_map);
|
||||
mymtd = do_map_probe("cfi_probe", &rpxlite_map);
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
mtd_device_register(mymtd, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap((void *)rpxlite_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void __exit cleanup_rpxlite(void)
|
||||
{
|
||||
if (mymtd) {
|
||||
mtd_device_unregister(mymtd);
|
||||
map_destroy(mymtd);
|
||||
}
|
||||
if (rpxlite_map.virt) {
|
||||
iounmap((void *)rpxlite_map.virt);
|
||||
rpxlite_map.virt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_rpxlite);
|
||||
module_exit(cleanup_rpxlite);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arnold Christensen <AKC@pel.dk>");
|
||||
MODULE_DESCRIPTION("MTD map driver for RPX Lite and CLLF boards");
|
|
@ -244,7 +244,7 @@ static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
|
||||
static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
|
||||
|
||||
static int sa1100_mtd_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ struct map_info soleng_flash_map = {
|
|||
.bankwidth = 4,
|
||||
};
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
#ifdef CONFIG_MTD_SUPERH_RESERVE
|
||||
static struct mtd_partition superh_se_partitions[] = {
|
||||
|
|
|
@ -1,249 +0,0 @@
|
|||
/*
|
||||
* Handle mapping of the flash memory access routines
|
||||
* on TQM8xxL based devices.
|
||||
*
|
||||
* based on rpxlite.c
|
||||
*
|
||||
* Copyright(C) 2001 Kirk Lee <kirk@hpc.ee.ntu.edu.tw>
|
||||
*
|
||||
* This code is GPLed
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* According to TQM8xxL hardware manual, TQM8xxL series have
|
||||
* following flash memory organisations:
|
||||
* | capacity | | chip type | | bank0 | | bank1 |
|
||||
* 2MiB 512Kx16 2MiB 0
|
||||
* 4MiB 1Mx16 4MiB 0
|
||||
* 8MiB 1Mx16 4MiB 4MiB
|
||||
* Thus, we choose CONFIG_MTD_CFI_I2 & CONFIG_MTD_CFI_B4 at
|
||||
* kernel configuration.
|
||||
*/
|
||||
#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>
|
||||
|
||||
#define FLASH_ADDR 0x40000000
|
||||
#define FLASH_SIZE 0x00800000
|
||||
#define FLASH_BANK_MAX 4
|
||||
|
||||
// trivial struct to describe partition information
|
||||
struct mtd_part_def
|
||||
{
|
||||
int nums;
|
||||
unsigned char *type;
|
||||
struct mtd_partition* mtd_part;
|
||||
};
|
||||
|
||||
//static struct mtd_info *mymtd;
|
||||
static struct mtd_info* mtd_banks[FLASH_BANK_MAX];
|
||||
static struct map_info* map_banks[FLASH_BANK_MAX];
|
||||
static struct mtd_part_def part_banks[FLASH_BANK_MAX];
|
||||
static unsigned long num_banks;
|
||||
static void __iomem *start_scan_addr;
|
||||
|
||||
/*
|
||||
* Here are partition information for all known TQM8xxL series devices.
|
||||
* See include/linux/mtd/partitions.h for definition of the mtd_partition
|
||||
* structure.
|
||||
*
|
||||
* The *_max_flash_size is the maximum possible mapped flash size which
|
||||
* is not necessarily the actual flash size. It must correspond to the
|
||||
* value specified in the mapping definition defined by the
|
||||
* "struct map_desc *_io_desc" for the corresponding machine.
|
||||
*/
|
||||
|
||||
/* Currently, TQM8xxL has up to 8MiB flash */
|
||||
static unsigned long tqm8xxl_max_flash_size = 0x00800000;
|
||||
|
||||
/* partition definition for first flash bank
|
||||
* (cf. "drivers/char/flash_config.c")
|
||||
*/
|
||||
static struct mtd_partition tqm8xxl_partitions[] = {
|
||||
{
|
||||
.name = "ppcboot",
|
||||
.offset = 0x00000000,
|
||||
.size = 0x00020000, /* 128KB */
|
||||
.mask_flags = MTD_WRITEABLE, /* force read-only */
|
||||
},
|
||||
{
|
||||
.name = "kernel", /* default kernel image */
|
||||
.offset = 0x00020000,
|
||||
.size = 0x000e0000,
|
||||
.mask_flags = MTD_WRITEABLE, /* force read-only */
|
||||
},
|
||||
{
|
||||
.name = "user",
|
||||
.offset = 0x00100000,
|
||||
.size = 0x00100000,
|
||||
},
|
||||
{
|
||||
.name = "initrd",
|
||||
.offset = 0x00200000,
|
||||
.size = 0x00200000,
|
||||
}
|
||||
};
|
||||
/* partition definition for second flash bank */
|
||||
static struct mtd_partition tqm8xxl_fs_partitions[] = {
|
||||
{
|
||||
.name = "cramfs",
|
||||
.offset = 0x00000000,
|
||||
.size = 0x00200000,
|
||||
},
|
||||
{
|
||||
.name = "jffs",
|
||||
.offset = 0x00200000,
|
||||
.size = 0x00200000,
|
||||
//.size = MTDPART_SIZ_FULL,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init init_tqm_mtd(void)
|
||||
{
|
||||
int idx = 0, ret = 0;
|
||||
unsigned long flash_addr, flash_size, mtd_size = 0;
|
||||
/* pointer to TQM8xxL board info data */
|
||||
bd_t *bd = (bd_t *)__res;
|
||||
|
||||
flash_addr = bd->bi_flashstart;
|
||||
flash_size = bd->bi_flashsize;
|
||||
|
||||
//request maximum flash size address space
|
||||
start_scan_addr = ioremap(flash_addr, flash_size);
|
||||
if (!start_scan_addr) {
|
||||
printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __func__, flash_addr);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
|
||||
if(mtd_size >= flash_size)
|
||||
break;
|
||||
|
||||
printk(KERN_INFO "%s: chip probing count %d\n", __func__, idx);
|
||||
|
||||
map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
|
||||
if(map_banks[idx] == NULL) {
|
||||
ret = -ENOMEM;
|
||||
/* FIXME: What if some MTD devices were probed already? */
|
||||
goto error_mem;
|
||||
}
|
||||
|
||||
map_banks[idx]->name = kmalloc(16, GFP_KERNEL);
|
||||
|
||||
if (!map_banks[idx]->name) {
|
||||
ret = -ENOMEM;
|
||||
/* FIXME: What if some MTD devices were probed already? */
|
||||
goto error_mem;
|
||||
}
|
||||
sprintf(map_banks[idx]->name, "TQM8xxL%d", idx);
|
||||
|
||||
map_banks[idx]->size = flash_size;
|
||||
map_banks[idx]->bankwidth = 4;
|
||||
|
||||
simple_map_init(map_banks[idx]);
|
||||
|
||||
map_banks[idx]->virt = start_scan_addr;
|
||||
map_banks[idx]->phys = flash_addr;
|
||||
/* FIXME: This looks utterly bogus, but I'm trying to
|
||||
preserve the behaviour of the original (shown here)...
|
||||
|
||||
map_banks[idx]->map_priv_1 =
|
||||
start_scan_addr + ((idx > 0) ?
|
||||
(mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
|
||||
*/
|
||||
|
||||
if (idx && mtd_banks[idx-1]) {
|
||||
map_banks[idx]->virt += mtd_banks[idx-1]->size;
|
||||
map_banks[idx]->phys += mtd_banks[idx-1]->size;
|
||||
}
|
||||
|
||||
//start to probe flash chips
|
||||
mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
|
||||
|
||||
if (mtd_banks[idx]) {
|
||||
mtd_banks[idx]->owner = THIS_MODULE;
|
||||
mtd_size += mtd_banks[idx]->size;
|
||||
num_banks++;
|
||||
|
||||
printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __func__, num_banks,
|
||||
mtd_banks[idx]->name, mtd_banks[idx]->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* no supported flash chips found */
|
||||
if (!num_banks) {
|
||||
printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n");
|
||||
ret = -ENXIO;
|
||||
goto error_mem;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select Static partition definitions
|
||||
*/
|
||||
part_banks[0].mtd_part = tqm8xxl_partitions;
|
||||
part_banks[0].type = "Static image";
|
||||
part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions);
|
||||
|
||||
part_banks[1].mtd_part = tqm8xxl_fs_partitions;
|
||||
part_banks[1].type = "Static file system";
|
||||
part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions);
|
||||
|
||||
for(idx = 0; idx < num_banks ; idx++) {
|
||||
if (part_banks[idx].nums == 0)
|
||||
printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx);
|
||||
else
|
||||
printk(KERN_NOTICE "TQM flash%d: Using %s partition definition\n",
|
||||
idx, part_banks[idx].type);
|
||||
mtd_device_register(mtd_banks[idx], part_banks[idx].mtd_part,
|
||||
part_banks[idx].nums);
|
||||
}
|
||||
return 0;
|
||||
error_mem:
|
||||
for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
|
||||
if(map_banks[idx] != NULL) {
|
||||
kfree(map_banks[idx]->name);
|
||||
map_banks[idx]->name = NULL;
|
||||
kfree(map_banks[idx]);
|
||||
map_banks[idx] = NULL;
|
||||
}
|
||||
}
|
||||
error:
|
||||
iounmap(start_scan_addr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cleanup_tqm_mtd(void)
|
||||
{
|
||||
unsigned int idx = 0;
|
||||
for(idx = 0 ; idx < num_banks ; idx++) {
|
||||
/* destroy mtd_info previously allocated */
|
||||
if (mtd_banks[idx]) {
|
||||
mtd_device_unregister(mtd_banks[idx]);
|
||||
map_destroy(mtd_banks[idx]);
|
||||
}
|
||||
/* release map_info not used anymore */
|
||||
kfree(map_banks[idx]->name);
|
||||
kfree(map_banks[idx]);
|
||||
}
|
||||
|
||||
if (start_scan_addr) {
|
||||
iounmap(start_scan_addr);
|
||||
start_scan_addr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_tqm_mtd);
|
||||
module_exit(cleanup_tqm_mtd);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kirk Lee <kirk@hpc.ee.ntu.edu.tw>");
|
||||
MODULE_DESCRIPTION("MTD map driver for TQM8xxL boards");
|
|
@ -82,11 +82,12 @@ static void __exit cleanup_tsunami_flash(void)
|
|||
tsunami_flash_mtd = 0;
|
||||
}
|
||||
|
||||
static const char * const rom_probe_types[] = {
|
||||
"cfi_probe", "jedec_probe", "map_rom", NULL };
|
||||
|
||||
static int __init init_tsunami_flash(void)
|
||||
{
|
||||
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
|
||||
char **type;
|
||||
const char * const *type;
|
||||
|
||||
tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT);
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "mtdcore.h"
|
||||
|
||||
static DEFINE_MUTEX(mtd_mutex);
|
||||
|
||||
/*
|
||||
|
@ -365,37 +367,35 @@ static void mtdchar_erase_callback (struct erase_info *instr)
|
|||
wake_up((wait_queue_head_t *)instr->priv);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_MTD_OTP
|
||||
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
|
||||
{
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
size_t retlen;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Make a fake call to mtd_read_fact_prot_reg() to check if OTP
|
||||
* operations are supported.
|
||||
*/
|
||||
if (mtd_read_fact_prot_reg(mtd, -1, 0, &retlen, NULL) == -EOPNOTSUPP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (mode) {
|
||||
case MTD_OTP_FACTORY:
|
||||
if (mtd_read_fact_prot_reg(mtd, -1, 0, &retlen, NULL) ==
|
||||
-EOPNOTSUPP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mfi->mode = MTD_FILE_MODE_OTP_FACTORY;
|
||||
break;
|
||||
case MTD_OTP_USER:
|
||||
if (mtd_read_user_prot_reg(mtd, -1, 0, &retlen, NULL) ==
|
||||
-EOPNOTSUPP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
mfi->mode = MTD_FILE_MODE_OTP_USER;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
case MTD_OTP_OFF:
|
||||
mfi->mode = MTD_FILE_MODE_NORMAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
# define otp_select_filemode(f,m) -EOPNOTSUPP
|
||||
#endif
|
||||
|
||||
static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
|
||||
uint64_t start, uint32_t length, void __user *ptr,
|
||||
|
@ -888,7 +888,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAVE_MTD_OTP
|
||||
case OTPSELECT:
|
||||
{
|
||||
int mode;
|
||||
|
@ -944,7 +943,6 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
|
|||
ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* This ioctl is being deprecated - it truncates the ECC layout */
|
||||
case ECCGETLAYOUT:
|
||||
|
@ -1185,23 +1183,25 @@ static struct file_system_type mtd_inodefs_type = {
|
|||
};
|
||||
MODULE_ALIAS_FS("mtd_inodefs");
|
||||
|
||||
static int __init init_mtdchar(void)
|
||||
int __init init_mtdchar(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
|
||||
"mtd", &mtd_fops);
|
||||
if (ret < 0) {
|
||||
pr_notice("Can't allocate major number %d for "
|
||||
"Memory Technology Devices.\n", MTD_CHAR_MAJOR);
|
||||
pr_err("Can't allocate major number %d for MTD\n",
|
||||
MTD_CHAR_MAJOR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = register_filesystem(&mtd_inodefs_type);
|
||||
if (ret) {
|
||||
pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret);
|
||||
pr_err("Can't register mtd_inodefs filesystem, error %d\n",
|
||||
ret);
|
||||
goto err_unregister_chdev;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_unregister_chdev:
|
||||
|
@ -1209,18 +1209,10 @@ static int __init init_mtdchar(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mtdchar(void)
|
||||
void __exit cleanup_mtdchar(void)
|
||||
{
|
||||
unregister_filesystem(&mtd_inodefs_type);
|
||||
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
|
||||
}
|
||||
|
||||
module_init(init_mtdchar);
|
||||
module_exit(cleanup_mtdchar);
|
||||
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
MODULE_DESCRIPTION("Direct character-device access to MTD devices");
|
||||
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include "mtdcore.h"
|
||||
|
||||
/*
|
||||
* backing device capabilities for non-mappable devices (such as NAND flash)
|
||||
* - permits private mappings, copies are taken of the data
|
||||
|
@ -97,11 +98,7 @@ EXPORT_SYMBOL_GPL(__mtd_next_device);
|
|||
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...
|
||||
|
@ -493,7 +490,7 @@ int del_mtd_device(struct mtd_info *mtd)
|
|||
*
|
||||
* Returns zero in case of success and a negative error code in case of failure.
|
||||
*/
|
||||
int mtd_device_parse_register(struct mtd_info *mtd, const char **types,
|
||||
int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
|
||||
struct mtd_part_parser_data *parser_data,
|
||||
const struct mtd_partition *parts,
|
||||
int nr_parts)
|
||||
|
@ -1117,8 +1114,6 @@ EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to);
|
|||
/*====================================================================*/
|
||||
/* Support for /proc/mtd */
|
||||
|
||||
static struct proc_dir_entry *proc_mtd;
|
||||
|
||||
static int mtd_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
|
@ -1164,6 +1159,8 @@ static int __init mtd_bdi_init(struct backing_dev_info *bdi, const char *name)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct proc_dir_entry *proc_mtd;
|
||||
|
||||
static int __init init_mtd(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1184,11 +1181,17 @@ static int __init init_mtd(void)
|
|||
if (ret)
|
||||
goto err_bdi3;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
ret = init_mtdchar();
|
||||
if (ret)
|
||||
goto out_procfs;
|
||||
|
||||
return 0;
|
||||
|
||||
out_procfs:
|
||||
if (proc_mtd)
|
||||
remove_proc_entry("mtd", NULL);
|
||||
err_bdi3:
|
||||
bdi_destroy(&mtd_bdi_ro_mappable);
|
||||
err_bdi2:
|
||||
|
@ -1202,10 +1205,9 @@ static int __init init_mtd(void)
|
|||
|
||||
static void __exit cleanup_mtd(void)
|
||||
{
|
||||
#ifdef CONFIG_PROC_FS
|
||||
cleanup_mtdchar();
|
||||
if (proc_mtd)
|
||||
remove_proc_entry( "mtd", NULL);
|
||||
#endif /* CONFIG_PROC_FS */
|
||||
remove_proc_entry("mtd", NULL);
|
||||
class_unregister(&mtd_class);
|
||||
bdi_destroy(&mtd_bdi_unmappable);
|
||||
bdi_destroy(&mtd_bdi_ro_mappable);
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
/* linux/drivers/mtd/mtdcore.h
|
||||
*
|
||||
* Header file for driver private mtdcore exports
|
||||
*
|
||||
/*
|
||||
* These are exported solely for the purpose of mtd_blkdevs.c and mtdchar.c.
|
||||
* You should not use them for _anything_ else.
|
||||
*/
|
||||
|
||||
/* These are exported solely for the purpose of mtd_blkdevs.c. You
|
||||
should not use them for _anything_ else */
|
||||
|
||||
extern struct mutex mtd_table_mutex;
|
||||
extern struct mtd_info *__mtd_next_device(int i);
|
||||
|
||||
extern int add_mtd_device(struct mtd_info *mtd);
|
||||
extern int del_mtd_device(struct mtd_info *mtd);
|
||||
extern int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *,
|
||||
int);
|
||||
extern int del_mtd_partitions(struct mtd_info *);
|
||||
extern int parse_mtd_partitions(struct mtd_info *master, const char **types,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data);
|
||||
struct mtd_info *__mtd_next_device(int i);
|
||||
int add_mtd_device(struct mtd_info *mtd);
|
||||
int del_mtd_device(struct mtd_info *mtd);
|
||||
int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
|
||||
int del_mtd_partitions(struct mtd_info *);
|
||||
int parse_mtd_partitions(struct mtd_info *master, const char * const *types,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data);
|
||||
|
||||
int __init init_mtdchar(void);
|
||||
void __exit cleanup_mtdchar(void);
|
||||
|
||||
#define mtd_for_each_device(mtd) \
|
||||
for ((mtd) = __mtd_next_device(0); \
|
||||
|
|
|
@ -694,7 +694,7 @@ EXPORT_SYMBOL_GPL(deregister_mtd_parser);
|
|||
* Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
|
||||
* are changing this array!
|
||||
*/
|
||||
static const char *default_mtd_part_types[] = {
|
||||
static const char * const default_mtd_part_types[] = {
|
||||
"cmdlinepart",
|
||||
"ofpart",
|
||||
NULL
|
||||
|
@ -720,7 +720,7 @@ static const char *default_mtd_part_types[] = {
|
|||
* o a positive number of found partitions, in which case on exit @pparts will
|
||||
* point to an array containing this number of &struct mtd_info objects.
|
||||
*/
|
||||
int parse_mtd_partitions(struct mtd_info *master, const char **types,
|
||||
int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
|
||||
struct mtd_partition **pparts,
|
||||
struct mtd_part_parser_data *data)
|
||||
{
|
||||
|
|
|
@ -41,14 +41,6 @@ config MTD_SM_COMMON
|
|||
tristate
|
||||
default n
|
||||
|
||||
config MTD_NAND_MUSEUM_IDS
|
||||
bool "Enable chip ids for obsolete ancient NAND devices"
|
||||
default n
|
||||
help
|
||||
Enable this option only when your board has first generation
|
||||
NAND chips (page size 256 byte, erase size 4-8KiB). The IDs
|
||||
of these chips were reused by later, larger chips.
|
||||
|
||||
config MTD_NAND_DENALI
|
||||
tristate "Support Denali NAND controller"
|
||||
help
|
||||
|
@ -81,12 +73,6 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
|
|||
scratch register here to enable this feature. On Intel Moorestown
|
||||
boards, the scratch register is at 0xFF108018.
|
||||
|
||||
config MTD_NAND_H1900
|
||||
tristate "iPAQ H1900 flash"
|
||||
depends on ARCH_PXA && BROKEN
|
||||
help
|
||||
This enables the driver for the iPAQ h1900 flash.
|
||||
|
||||
config MTD_NAND_GPIO
|
||||
tristate "GPIO NAND Flash driver"
|
||||
depends on GPIOLIB && ARM
|
||||
|
@ -201,22 +187,6 @@ config MTD_NAND_BF5XX_BOOTROM_ECC
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MTD_NAND_RTC_FROM4
|
||||
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
|
||||
depends on SH_SOLUTION_ENGINE
|
||||
select REED_SOLOMON
|
||||
select REED_SOLOMON_DEC8
|
||||
select BITREVERSE
|
||||
help
|
||||
This enables the driver for the Renesas Technology AG-AND
|
||||
flash interface board (FROM_BOARD4)
|
||||
|
||||
config MTD_NAND_PPCHAMELEONEVB
|
||||
tristate "NAND Flash device on PPChameleonEVB board"
|
||||
depends on PPCHAMELEONEVB && BROKEN
|
||||
help
|
||||
This enables the NAND flash driver on the PPChameleon EVB Board.
|
||||
|
||||
config MTD_NAND_S3C2410
|
||||
tristate "NAND Flash support for Samsung S3C SoCs"
|
||||
depends on ARCH_S3C24XX || ARCH_S3C64XX
|
||||
|
|
|
@ -15,14 +15,11 @@ obj-$(CONFIG_MTD_NAND_DENALI_PCI) += denali_pci.o
|
|||
obj-$(CONFIG_MTD_NAND_DENALI_DT) += denali_dt.o
|
||||
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_DOCG4) += docg4.o
|
||||
obj-$(CONFIG_MTD_NAND_FSMC) += fsmc_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
|
||||
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
|
||||
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
|
||||
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
|
||||
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
||||
|
|
|
@ -1737,20 +1737,7 @@ static struct platform_driver atmel_nand_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init atmel_nand_init(void)
|
||||
{
|
||||
return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe);
|
||||
}
|
||||
|
||||
|
||||
static void __exit atmel_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&atmel_nand_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(atmel_nand_init);
|
||||
module_exit(atmel_nand_exit);
|
||||
module_platform_driver_probe(atmel_nand_driver, atmel_nand_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Rick Bronson");
|
||||
|
|
|
@ -874,21 +874,7 @@ static struct platform_driver bf5xx_nand_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init bf5xx_nand_init(void)
|
||||
{
|
||||
printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n",
|
||||
DRV_DESC, DRV_VERSION);
|
||||
|
||||
return platform_driver_register(&bf5xx_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit bf5xx_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bf5xx_nand_driver);
|
||||
}
|
||||
|
||||
module_init(bf5xx_nand_init);
|
||||
module_exit(bf5xx_nand_exit);
|
||||
module_platform_driver(bf5xx_nand_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR(DRV_AUTHOR);
|
||||
|
|
|
@ -303,13 +303,7 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_RNDIN:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_DEPLETE1:
|
||||
case NAND_CMD_RNDOUT:
|
||||
case NAND_CMD_STATUS_ERROR:
|
||||
case NAND_CMD_STATUS_ERROR0:
|
||||
case NAND_CMD_STATUS_ERROR1:
|
||||
case NAND_CMD_STATUS_ERROR2:
|
||||
case NAND_CMD_STATUS_ERROR3:
|
||||
cafe_writel(cafe, cafe->ctl2, NAND_CTRL2);
|
||||
return;
|
||||
}
|
||||
|
@ -536,8 +530,8 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page,
|
||||
int cached, int raw)
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw)
|
||||
{
|
||||
int status;
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/platform_data/mtd-davinci.h>
|
||||
#include <linux/platform_data/mtd-davinci-aemif.h>
|
||||
|
@ -577,7 +578,6 @@ static struct davinci_nand_pdata
|
|||
return pdev->dev.platform_data;
|
||||
}
|
||||
#else
|
||||
#define davinci_nand_of_match NULL
|
||||
static struct davinci_nand_pdata
|
||||
*nand_davinci_get_pdata(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -878,22 +878,12 @@ static struct platform_driver nand_davinci_driver = {
|
|||
.driver = {
|
||||
.name = "davinci_nand",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = davinci_nand_of_match,
|
||||
.of_match_table = of_match_ptr(davinci_nand_of_match),
|
||||
},
|
||||
};
|
||||
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_platform_driver_probe(nand_davinci_driver, nand_davinci_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Texas Instruments");
|
||||
|
|
|
@ -42,7 +42,7 @@ static void __iomem *request_and_map(struct device *dev,
|
|||
}
|
||||
|
||||
ptr = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||
if (!res)
|
||||
if (!ptr)
|
||||
dev_err(dev, "ioremap_nocache of %s failed!", res->name);
|
||||
|
||||
return ptr;
|
||||
|
@ -90,7 +90,7 @@ static int denali_dt_probe(struct platform_device *ofdev)
|
|||
denali->irq = platform_get_irq(ofdev, 0);
|
||||
if (denali->irq < 0) {
|
||||
dev_err(&ofdev->dev, "no irq defined\n");
|
||||
return -ENXIO;
|
||||
return denali->irq;
|
||||
}
|
||||
|
||||
denali->flash_reg = request_and_map(&ofdev->dev, denali_reg);
|
||||
|
@ -146,21 +146,11 @@ static struct platform_driver denali_dt_driver = {
|
|||
.driver = {
|
||||
.name = "denali-nand-dt",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(denali_nand_dt_ids),
|
||||
.of_match_table = denali_nand_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init denali_init_dt(void)
|
||||
{
|
||||
return platform_driver_register(&denali_dt_driver);
|
||||
}
|
||||
module_init(denali_init_dt);
|
||||
|
||||
static void __exit denali_exit_dt(void)
|
||||
{
|
||||
platform_driver_unregister(&denali_dt_driver);
|
||||
}
|
||||
module_exit(denali_exit_dt);
|
||||
module_platform_driver(denali_dt_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jamie Iles");
|
||||
|
|
|
@ -1397,18 +1397,7 @@ static struct platform_driver docg4_driver = {
|
|||
.remove = __exit_p(cleanup_docg4),
|
||||
};
|
||||
|
||||
static int __init docg4_init(void)
|
||||
{
|
||||
return platform_driver_probe(&docg4_driver, probe_docg4);
|
||||
}
|
||||
|
||||
static void __exit docg4_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&docg4_driver);
|
||||
}
|
||||
|
||||
module_init(docg4_init);
|
||||
module_exit(docg4_exit);
|
||||
module_platform_driver_probe(docg4_driver, probe_docg4);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Mike Dunn");
|
||||
|
|
|
@ -1235,18 +1235,7 @@ static struct platform_driver fsmc_nand_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init fsmc_nand_init(void)
|
||||
{
|
||||
return platform_driver_probe(&fsmc_nand_driver,
|
||||
fsmc_nand_probe);
|
||||
}
|
||||
module_init(fsmc_nand_init);
|
||||
|
||||
static void __exit fsmc_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&fsmc_nand_driver);
|
||||
}
|
||||
module_exit(fsmc_nand_exit);
|
||||
module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
|
||||
|
|
|
@ -190,7 +190,6 @@ static struct resource *gpio_nand_get_io_sync_of(struct platform_device *pdev)
|
|||
return r;
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
#define gpio_nand_id_table NULL
|
||||
static inline int gpio_nand_get_config_of(const struct device *dev,
|
||||
struct gpio_nand_platdata *plat)
|
||||
{
|
||||
|
@ -259,8 +258,6 @@ static int gpio_nand_remove(struct platform_device *dev)
|
|||
if (gpio_is_valid(gpiomtd->plat.gpio_rdy))
|
||||
gpio_free(gpiomtd->plat.gpio_rdy);
|
||||
|
||||
kfree(gpiomtd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -297,7 +294,7 @@ static int gpio_nand_probe(struct platform_device *dev)
|
|||
if (!res0)
|
||||
return -EINVAL;
|
||||
|
||||
gpiomtd = kzalloc(sizeof(*gpiomtd), GFP_KERNEL);
|
||||
gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL);
|
||||
if (gpiomtd == NULL) {
|
||||
dev_err(&dev->dev, "failed to create NAND MTD\n");
|
||||
return -ENOMEM;
|
||||
|
@ -412,7 +409,6 @@ static int gpio_nand_probe(struct platform_device *dev)
|
|||
iounmap(gpiomtd->nand_chip.IO_ADDR_R);
|
||||
release_mem_region(res0->start, resource_size(res0));
|
||||
err_map:
|
||||
kfree(gpiomtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -421,7 +417,7 @@ static struct platform_driver gpio_nand_driver = {
|
|||
.remove = gpio_nand_remove,
|
||||
.driver = {
|
||||
.name = "gpio-nand",
|
||||
.of_match_table = gpio_nand_id_table,
|
||||
.of_match_table = of_match_ptr(gpio_nand_id_table),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
/*
|
||||
* drivers/mtd/nand/h1910.c
|
||||
*
|
||||
* Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
|
||||
*
|
||||
* Derived from drivers/mtd/nand/edb7312.c
|
||||
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
||||
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash device found on the
|
||||
* iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is
|
||||
* a 128Mibit (16MiB x 8 bits) NAND flash device.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <asm/sizes.h>
|
||||
#include <mach/h1900-gpio.h>
|
||||
#include <mach/ipaq.h>
|
||||
|
||||
/*
|
||||
* MTD structure for EDB7312 board
|
||||
*/
|
||||
static struct mtd_info *h1910_nand_mtd = NULL;
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
|
||||
/*
|
||||
* Define static partitions for flash device
|
||||
*/
|
||||
static struct mtd_partition partition_info[] = {
|
||||
{name:"h1910 NAND Flash",
|
||||
offset:0,
|
||||
size:16 * 1024 * 1024}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*
|
||||
* NAND_NCE: bit 0 - don't care
|
||||
* NAND_CLE: bit 1 - address bit 2
|
||||
* NAND_ALE: bit 2 - address bit 3
|
||||
*/
|
||||
static void h1910_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W | ((ctrl & 0x6) << 1));
|
||||
}
|
||||
|
||||
/*
|
||||
* read device ready pin
|
||||
*/
|
||||
#if 0
|
||||
static int h1910_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return (GPLR(55) & GPIO_bit(55));
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init h1910_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
void __iomem *nandaddr;
|
||||
|
||||
if (!machine_is_h1900())
|
||||
return -ENODEV;
|
||||
|
||||
nandaddr = ioremap(0x08000000, 0x1000);
|
||||
if (!nandaddr) {
|
||||
printk("Failed to ioremap nand flash.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!h1910_nand_mtd) {
|
||||
printk("Unable to allocate h1910 NAND MTD device structure.\n");
|
||||
iounmap((void *)nandaddr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *)(&h1910_nand_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(h1910_nand_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
h1910_nand_mtd->priv = this;
|
||||
h1910_nand_mtd->owner = THIS_MODULE;
|
||||
|
||||
/*
|
||||
* Enable VPEN
|
||||
*/
|
||||
GPSR(37) = GPIO_bit(37);
|
||||
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = nandaddr;
|
||||
this->IO_ADDR_W = nandaddr;
|
||||
this->cmd_ctrl = h1910_hwcontrol;
|
||||
this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */
|
||||
/* 15 us command delay time */
|
||||
this->chip_delay = 50;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(h1910_nand_mtd, 1)) {
|
||||
printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
|
||||
kfree(h1910_nand_mtd);
|
||||
iounmap((void *)nandaddr);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(h1910_nand_mtd, NULL, NULL, partition_info,
|
||||
NUM_PARTITIONS);
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(h1910_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit h1910_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this = (struct nand_chip *)&h1910_nand_mtd[1];
|
||||
|
||||
/* Release resources, unregister device */
|
||||
nand_release(h1910_nand_mtd);
|
||||
|
||||
/* Release io resource */
|
||||
iounmap((void *)this->IO_ADDR_W);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree(h1910_nand_mtd);
|
||||
}
|
||||
|
||||
module_exit(h1910_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Joshua Wise <joshua at joshuawise dot com>");
|
||||
MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910");
|
|
@ -540,8 +540,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
static int lpc32xx_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page,
|
||||
int cached, int raw)
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw)
|
||||
{
|
||||
int res;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* Overview:
|
||||
* This is the generic MTD driver for NAND flash devices. It should be
|
||||
* capable of working with almost all NAND chips currently available.
|
||||
* Basic support for AG-AND chips is provided.
|
||||
*
|
||||
* Additional technical information is available on
|
||||
* http://www.linux-mtd.infradead.org/doc/nand.html
|
||||
|
@ -22,8 +21,6 @@
|
|||
* Enable cached programming for 2k page size chips
|
||||
* Check, if mtd->ecctype should be set to MTD_ECC_HW
|
||||
* if we have HW ECC support.
|
||||
* The AG-AND chips have nice features for speed improvement,
|
||||
* which are not supported yet. Read / program 4 pages in one go.
|
||||
* BBT table is not serialized, has to be fixed
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -515,7 +512,7 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
|
|||
* @page_addr: the page address for this command, -1 if none
|
||||
*
|
||||
* Send command to NAND device. This function is used for small page devices
|
||||
* (256/512 Bytes per page).
|
||||
* (512 Bytes per page).
|
||||
*/
|
||||
static void nand_command(struct mtd_info *mtd, unsigned int command,
|
||||
int column, int page_addr)
|
||||
|
@ -631,8 +628,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|||
}
|
||||
|
||||
/* Command latch cycle */
|
||||
chip->cmd_ctrl(mtd, command & 0xff,
|
||||
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
||||
chip->cmd_ctrl(mtd, command, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
|
||||
|
||||
if (column != -1 || page_addr != -1) {
|
||||
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
|
||||
|
@ -671,16 +667,6 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_RNDIN:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_DEPLETE1:
|
||||
return;
|
||||
|
||||
case NAND_CMD_STATUS_ERROR:
|
||||
case NAND_CMD_STATUS_ERROR0:
|
||||
case NAND_CMD_STATUS_ERROR1:
|
||||
case NAND_CMD_STATUS_ERROR2:
|
||||
case NAND_CMD_STATUS_ERROR3:
|
||||
/* Read error status commands require only a short delay */
|
||||
udelay(chip->chip_delay);
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
|
@ -836,10 +822,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|||
*/
|
||||
ndelay(100);
|
||||
|
||||
if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
|
||||
chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
|
||||
else
|
||||
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
||||
chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
|
||||
|
||||
if (in_interrupt() || oops_in_progress)
|
||||
panic_nand_wait(mtd, chip, timeo);
|
||||
|
@ -1127,7 +1110,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
}
|
||||
|
||||
/**
|
||||
* nand_read_subpage - [REPLACEABLE] software ECC based sub-page read function
|
||||
* nand_read_subpage - [REPLACEABLE] ECC based sub-page read function
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @data_offs: offset of requested data within the page
|
||||
|
@ -1995,6 +1978,67 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write
|
||||
* @mtd: mtd info structure
|
||||
* @chip: nand chip info structure
|
||||
* @column: column address of subpage within the page
|
||||
* @data_len: data length
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
*/
|
||||
static int nand_write_subpage_hwecc(struct mtd_info *mtd,
|
||||
struct nand_chip *chip, uint32_t offset,
|
||||
uint32_t data_len, const uint8_t *data_buf,
|
||||
int oob_required)
|
||||
{
|
||||
uint8_t *oob_buf = chip->oob_poi;
|
||||
uint8_t *ecc_calc = chip->buffers->ecccalc;
|
||||
int ecc_size = chip->ecc.size;
|
||||
int ecc_bytes = chip->ecc.bytes;
|
||||
int ecc_steps = chip->ecc.steps;
|
||||
uint32_t *eccpos = chip->ecc.layout->eccpos;
|
||||
uint32_t start_step = offset / ecc_size;
|
||||
uint32_t end_step = (offset + data_len - 1) / ecc_size;
|
||||
int oob_bytes = mtd->oobsize / ecc_steps;
|
||||
int step, i;
|
||||
|
||||
for (step = 0; step < ecc_steps; step++) {
|
||||
/* configure controller for WRITE access */
|
||||
chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
|
||||
|
||||
/* write data (untouched subpages already masked by 0xFF) */
|
||||
chip->write_buf(mtd, data_buf, ecc_size);
|
||||
|
||||
/* mask ECC of un-touched subpages by padding 0xFF */
|
||||
if ((step < start_step) || (step > end_step))
|
||||
memset(ecc_calc, 0xff, ecc_bytes);
|
||||
else
|
||||
chip->ecc.calculate(mtd, data_buf, ecc_calc);
|
||||
|
||||
/* mask OOB of un-touched subpages by padding 0xFF */
|
||||
/* if oob_required, preserve OOB metadata of written subpage */
|
||||
if (!oob_required || (step < start_step) || (step > end_step))
|
||||
memset(oob_buf, 0xff, oob_bytes);
|
||||
|
||||
data_buf += ecc_size;
|
||||
ecc_calc += ecc_bytes;
|
||||
oob_buf += oob_bytes;
|
||||
}
|
||||
|
||||
/* copy calculated ECC for whole page to chip->buffer->oob */
|
||||
/* this include masked-value(0xFF) for unwritten subpages */
|
||||
ecc_calc = chip->buffers->ecccalc;
|
||||
for (i = 0; i < chip->ecc.total; i++)
|
||||
chip->oob_poi[eccpos[i]] = ecc_calc[i];
|
||||
|
||||
/* write OOB buffer to NAND device */
|
||||
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nand_write_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page write
|
||||
* @mtd: mtd info structure
|
||||
|
@ -2047,6 +2091,8 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
|
|||
* nand_write_page - [REPLACEABLE] write one page
|
||||
* @mtd: MTD device structure
|
||||
* @chip: NAND chip descriptor
|
||||
* @offset: address offset within the page
|
||||
* @data_len: length of actual data to be written
|
||||
* @buf: the data to write
|
||||
* @oob_required: must write chip->oob_poi to OOB
|
||||
* @page: page number to write
|
||||
|
@ -2054,15 +2100,25 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
|
|||
* @raw: use _raw version of write_page
|
||||
*/
|
||||
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page,
|
||||
int cached, int raw)
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw)
|
||||
{
|
||||
int status;
|
||||
int status, subpage;
|
||||
|
||||
if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
|
||||
chip->ecc.write_subpage)
|
||||
subpage = offset || (data_len < mtd->writesize);
|
||||
else
|
||||
subpage = 0;
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
|
||||
|
||||
if (unlikely(raw))
|
||||
status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
|
||||
status = chip->ecc.write_page_raw(mtd, chip, buf,
|
||||
oob_required);
|
||||
else if (subpage)
|
||||
status = chip->ecc.write_subpage(mtd, chip, offset, data_len,
|
||||
buf, oob_required);
|
||||
else
|
||||
status = chip->ecc.write_page(mtd, chip, buf, oob_required);
|
||||
|
||||
|
@ -2075,7 +2131,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
*/
|
||||
cached = 0;
|
||||
|
||||
if (!cached || !(chip->options & NAND_CACHEPRG)) {
|
||||
if (!cached || !NAND_HAS_CACHEPROG(chip)) {
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
|
||||
status = chip->waitfunc(mtd, chip);
|
||||
|
@ -2176,7 +2232,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
|
||||
uint8_t *oob = ops->oobbuf;
|
||||
uint8_t *buf = ops->datbuf;
|
||||
int ret, subpage;
|
||||
int ret;
|
||||
int oob_required = oob ? 1 : 0;
|
||||
|
||||
ops->retlen = 0;
|
||||
|
@ -2191,10 +2247,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
}
|
||||
|
||||
column = to & (mtd->writesize - 1);
|
||||
subpage = column || (writelen & (mtd->writesize - 1));
|
||||
|
||||
if (subpage && oob)
|
||||
return -EINVAL;
|
||||
|
||||
chipnr = (int)(to >> chip->chip_shift);
|
||||
chip->select_chip(mtd, chipnr);
|
||||
|
@ -2243,9 +2295,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
|
|||
/* We still need to erase leftover OOB data */
|
||||
memset(chip->oob_poi, 0xff, mtd->oobsize);
|
||||
}
|
||||
|
||||
ret = chip->write_page(mtd, chip, wbuf, oob_required, page,
|
||||
cached, (ops->mode == MTD_OPS_RAW));
|
||||
ret = chip->write_page(mtd, chip, column, bytes, wbuf,
|
||||
oob_required, page, cached,
|
||||
(ops->mode == MTD_OPS_RAW));
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
|
@ -2480,24 +2532,6 @@ static void single_erase_cmd(struct mtd_info *mtd, int page)
|
|||
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* multi_erase_cmd - [GENERIC] AND specific block erase command function
|
||||
* @mtd: MTD device structure
|
||||
* @page: the page address of the block which will be erased
|
||||
*
|
||||
* AND multi block erase command function. Erase 4 consecutive blocks.
|
||||
*/
|
||||
static void multi_erase_cmd(struct mtd_info *mtd, int page)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
/* Send commands to erase a block */
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
|
||||
chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_erase - [MTD Interface] erase block(s)
|
||||
* @mtd: MTD device structure
|
||||
|
@ -2510,7 +2544,6 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
return nand_erase_nand(mtd, instr, 0);
|
||||
}
|
||||
|
||||
#define BBT_PAGE_MASK 0xffffff3f
|
||||
/**
|
||||
* nand_erase_nand - [INTERN] erase block(s)
|
||||
* @mtd: MTD device structure
|
||||
|
@ -2524,8 +2557,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|||
{
|
||||
int page, status, pages_per_block, ret, chipnr;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
loff_t rewrite_bbt[NAND_MAX_CHIPS] = {0};
|
||||
unsigned int bbt_masked_page = 0xffffffff;
|
||||
loff_t len;
|
||||
|
||||
pr_debug("%s: start = 0x%012llx, len = %llu\n",
|
||||
|
@ -2556,15 +2587,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|||
goto erase_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* If BBT requires refresh, set the BBT page mask to see if the BBT
|
||||
* should be rewritten. Otherwise the mask is set to 0xffffffff which
|
||||
* can not be matched. This is also done when the bbt is actually
|
||||
* erased to avoid recursive updates.
|
||||
*/
|
||||
if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
|
||||
bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
|
||||
|
||||
/* Loop through the pages */
|
||||
len = instr->len;
|
||||
|
||||
|
@ -2610,15 +2632,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|||
goto erase_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* If BBT requires refresh, set the BBT rewrite flag to the
|
||||
* page being erased.
|
||||
*/
|
||||
if (bbt_masked_page != 0xffffffff &&
|
||||
(page & BBT_PAGE_MASK) == bbt_masked_page)
|
||||
rewrite_bbt[chipnr] =
|
||||
((loff_t)page << chip->page_shift);
|
||||
|
||||
/* Increment page address and decrement length */
|
||||
len -= (1 << chip->phys_erase_shift);
|
||||
page += pages_per_block;
|
||||
|
@ -2628,15 +2641,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|||
chipnr++;
|
||||
chip->select_chip(mtd, -1);
|
||||
chip->select_chip(mtd, chipnr);
|
||||
|
||||
/*
|
||||
* If BBT requires refresh and BBT-PERCHIP, set the BBT
|
||||
* page mask to see if this BBT should be rewritten.
|
||||
*/
|
||||
if (bbt_masked_page != 0xffffffff &&
|
||||
(chip->bbt_td->options & NAND_BBT_PERCHIP))
|
||||
bbt_masked_page = chip->bbt_td->pages[chipnr] &
|
||||
BBT_PAGE_MASK;
|
||||
}
|
||||
}
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
@ -2653,23 +2657,6 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|||
if (!ret)
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
/*
|
||||
* If BBT requires refresh and erase was successful, rewrite any
|
||||
* selected bad block tables.
|
||||
*/
|
||||
if (bbt_masked_page == 0xffffffff || ret)
|
||||
return ret;
|
||||
|
||||
for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
|
||||
if (!rewrite_bbt[chipnr])
|
||||
continue;
|
||||
/* Update the BBT for chip */
|
||||
pr_debug("%s: nand_update_bbt (%d:0x%0llx 0x%0x)\n",
|
||||
__func__, chipnr, rewrite_bbt[chipnr],
|
||||
chip->bbt_td->pages[chipnr]);
|
||||
nand_update_bbt(mtd, rewrite_bbt[chipnr]);
|
||||
}
|
||||
|
||||
/* Return more or less happy */
|
||||
return ret;
|
||||
}
|
||||
|
@ -2905,8 +2892,6 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
|
|||
chip->onfi_version = 20;
|
||||
else if (val & (1 << 1))
|
||||
chip->onfi_version = 10;
|
||||
else
|
||||
chip->onfi_version = 0;
|
||||
|
||||
if (!chip->onfi_version) {
|
||||
pr_info("%s: unsupported ONFI version: %d\n", __func__, val);
|
||||
|
@ -3171,6 +3156,30 @@ static void nand_decode_bbm_options(struct mtd_info *mtd,
|
|||
chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
|
||||
}
|
||||
|
||||
static inline bool is_full_id_nand(struct nand_flash_dev *type)
|
||||
{
|
||||
return type->id_len;
|
||||
}
|
||||
|
||||
static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
struct nand_flash_dev *type, u8 *id_data, int *busw)
|
||||
{
|
||||
if (!strncmp(type->id, id_data, type->id_len)) {
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->erasesize = type->erasesize;
|
||||
mtd->oobsize = type->oobsize;
|
||||
|
||||
chip->cellinfo = id_data[2];
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
chip->options |= type->options;
|
||||
|
||||
*busw = type->options & NAND_BUSWIDTH_16;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the flash and manufacturer id and lookup if the type is supported.
|
||||
*/
|
||||
|
@ -3222,9 +3231,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|||
if (!type)
|
||||
type = nand_flash_ids;
|
||||
|
||||
for (; type->name != NULL; type++)
|
||||
if (*dev_id == type->id)
|
||||
break;
|
||||
for (; type->name != NULL; type++) {
|
||||
if (is_full_id_nand(type)) {
|
||||
if (find_full_id_nand(mtd, chip, type, id_data, &busw))
|
||||
goto ident_done;
|
||||
} else if (*dev_id == type->dev_id) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chip->onfi_version = 0;
|
||||
if (!type->name || !type->pagesize) {
|
||||
|
@ -3302,12 +3316,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
chip->badblockbits = 8;
|
||||
|
||||
/* Check for AND chips with 4 page planes */
|
||||
if (chip->options & NAND_4PAGE_ARRAY)
|
||||
chip->erase_cmd = multi_erase_cmd;
|
||||
else
|
||||
chip->erase_cmd = single_erase_cmd;
|
||||
chip->erase_cmd = single_erase_cmd;
|
||||
|
||||
/* Do not replace user supplied command function! */
|
||||
if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
|
||||
|
@ -3474,6 +3483,10 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|||
chip->ecc.read_oob = nand_read_oob_std;
|
||||
if (!chip->ecc.write_oob)
|
||||
chip->ecc.write_oob = nand_write_oob_std;
|
||||
if (!chip->ecc.read_subpage)
|
||||
chip->ecc.read_subpage = nand_read_subpage;
|
||||
if (!chip->ecc.write_subpage)
|
||||
chip->ecc.write_subpage = nand_write_subpage_hwecc;
|
||||
|
||||
case NAND_ECC_HW_SYNDROME:
|
||||
if ((!chip->ecc.calculate || !chip->ecc.correct ||
|
||||
|
|
|
@ -1240,15 +1240,6 @@ int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
|||
*/
|
||||
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
|
||||
|
||||
static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
|
||||
|
||||
static struct nand_bbt_descr agand_flashbased = {
|
||||
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
|
||||
.offs = 0x20,
|
||||
.len = 6,
|
||||
.pattern = scan_agand_pattern
|
||||
};
|
||||
|
||||
/* Generic flash bbt descriptors */
|
||||
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
|
||||
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
|
||||
|
@ -1333,22 +1324,6 @@ int nand_default_bbt(struct mtd_info *mtd)
|
|||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/*
|
||||
* Default for AG-AND. We must use a flash based bad block table as the
|
||||
* devices have factory marked _good_ blocks. Erasing those blocks
|
||||
* leads to loss of the good / bad information, so we _must_ store this
|
||||
* information in a good / bad table during startup.
|
||||
*/
|
||||
if (this->options & NAND_IS_AND) {
|
||||
/* Use the default pattern descriptors */
|
||||
if (!this->bbt_td) {
|
||||
this->bbt_td = &bbt_main_descr;
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
this->bbt_options |= NAND_BBT_USE_FLASH;
|
||||
return nand_scan_bbt(mtd, &agand_flashbased);
|
||||
}
|
||||
|
||||
/* Is a flash based bad block table requested? */
|
||||
if (this->bbt_options & NAND_BBT_USE_FLASH) {
|
||||
/* Use the default pattern descriptors */
|
||||
|
|
|
@ -10,163 +10,153 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
/*
|
||||
* Chip ID list
|
||||
*
|
||||
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
|
||||
* options
|
||||
*
|
||||
* Pagesize; 0, 256, 512
|
||||
* 0 get this information from the extended chip ID
|
||||
+ 256 256 Byte page size
|
||||
* 512 512 Byte page size
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
#define SP_OPTIONS NAND_NEED_READRDY
|
||||
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
|
||||
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, SP_OPTIONS},
|
||||
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, SP_OPTIONS},
|
||||
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, SP_OPTIONS},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, SP_OPTIONS},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, SP_OPTIONS},
|
||||
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, SP_OPTIONS},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, SP_OPTIONS},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, SP_OPTIONS},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, SP_OPTIONS},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, SP_OPTIONS},
|
||||
|
||||
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, SP_OPTIONS},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, SP_OPTIONS},
|
||||
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, SP_OPTIONS16},
|
||||
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, SP_OPTIONS16},
|
||||
#endif
|
||||
|
||||
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, SP_OPTIONS},
|
||||
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, SP_OPTIONS},
|
||||
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, SP_OPTIONS16},
|
||||
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, SP_OPTIONS16},
|
||||
|
||||
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, SP_OPTIONS},
|
||||
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, SP_OPTIONS},
|
||||
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, SP_OPTIONS16},
|
||||
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, SP_OPTIONS16},
|
||||
|
||||
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, SP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, SP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, SP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, SP_OPTIONS16},
|
||||
|
||||
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, SP_OPTIONS},
|
||||
{"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, SP_OPTIONS},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, SP_OPTIONS},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, SP_OPTIONS16},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, SP_OPTIONS16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, SP_OPTIONS16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, SP_OPTIONS16},
|
||||
|
||||
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, SP_OPTIONS},
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. The pagesize and the
|
||||
* erasesize is determined from the extended id bytes
|
||||
*/
|
||||
#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/* 512 Megabit */
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xD0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF0, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB0, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC0, 0, 64, 0, LP_OPTIONS16},
|
||||
|
||||
/* 1 Gigabit */
|
||||
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0xAD, 0, 128, 0, LP_OPTIONS16},
|
||||
|
||||
/* 2 Gigabit */
|
||||
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
|
||||
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
|
||||
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
|
||||
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
|
||||
|
||||
/* 4 Gigabit */
|
||||
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
|
||||
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
|
||||
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
|
||||
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
|
||||
|
||||
/* 8 Gigabit */
|
||||
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
|
||||
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
|
||||
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
|
||||
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
|
||||
|
||||
/* 16 Gigabit */
|
||||
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},
|
||||
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
|
||||
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
|
||||
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
|
||||
|
||||
/* 32 Gigabit */
|
||||
{"NAND 4GiB 1,8V 8-bit", 0xA7, 0, 4096, 0, LP_OPTIONS},
|
||||
{"NAND 4GiB 3,3V 8-bit", 0xD7, 0, 4096, 0, LP_OPTIONS},
|
||||
{"NAND 4GiB 1,8V 16-bit", 0xB7, 0, 4096, 0, LP_OPTIONS16},
|
||||
{"NAND 4GiB 3,3V 16-bit", 0xC7, 0, 4096, 0, LP_OPTIONS16},
|
||||
|
||||
/* 64 Gigabit */
|
||||
{"NAND 8GiB 1,8V 8-bit", 0xAE, 0, 8192, 0, LP_OPTIONS},
|
||||
{"NAND 8GiB 3,3V 8-bit", 0xDE, 0, 8192, 0, LP_OPTIONS},
|
||||
{"NAND 8GiB 1,8V 16-bit", 0xBE, 0, 8192, 0, LP_OPTIONS16},
|
||||
{"NAND 8GiB 3,3V 16-bit", 0xCE, 0, 8192, 0, LP_OPTIONS16},
|
||||
|
||||
/* 128 Gigabit */
|
||||
{"NAND 16GiB 1,8V 8-bit", 0x1A, 0, 16384, 0, LP_OPTIONS},
|
||||
{"NAND 16GiB 3,3V 8-bit", 0x3A, 0, 16384, 0, LP_OPTIONS},
|
||||
{"NAND 16GiB 1,8V 16-bit", 0x2A, 0, 16384, 0, LP_OPTIONS16},
|
||||
{"NAND 16GiB 3,3V 16-bit", 0x4A, 0, 16384, 0, LP_OPTIONS16},
|
||||
|
||||
/* 256 Gigabit */
|
||||
{"NAND 32GiB 1,8V 8-bit", 0x1C, 0, 32768, 0, LP_OPTIONS},
|
||||
{"NAND 32GiB 3,3V 8-bit", 0x3C, 0, 32768, 0, LP_OPTIONS},
|
||||
{"NAND 32GiB 1,8V 16-bit", 0x2C, 0, 32768, 0, LP_OPTIONS16},
|
||||
{"NAND 32GiB 3,3V 16-bit", 0x4C, 0, 32768, 0, LP_OPTIONS16},
|
||||
|
||||
/* 512 Gigabit */
|
||||
{"NAND 64GiB 1,8V 8-bit", 0x1E, 0, 65536, 0, LP_OPTIONS},
|
||||
{"NAND 64GiB 3,3V 8-bit", 0x3E, 0, 65536, 0, LP_OPTIONS},
|
||||
{"NAND 64GiB 1,8V 16-bit", 0x2E, 0, 65536, 0, LP_OPTIONS16},
|
||||
{"NAND 64GiB 3,3V 16-bit", 0x4E, 0, 65536, 0, LP_OPTIONS16},
|
||||
|
||||
/*
|
||||
* Renesas AND 1 Gigabit. Those chips do not support extended id and
|
||||
* have a strange page/block layout ! The chosen minimum erasesize is
|
||||
* 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page
|
||||
* planes 1 block = 2 pages, but due to plane arrangement the blocks
|
||||
* 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would
|
||||
* increase the eraseblock size so we chose a combined one which can be
|
||||
* erased in one go There are more speed improvements for reads and
|
||||
* writes possible, but not implemented now
|
||||
*/
|
||||
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
|
||||
NAND_IS_AND | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
|
||||
|
||||
{NULL,}
|
||||
};
|
||||
#define SP_OPTIONS NAND_NEED_READRDY
|
||||
#define SP_OPTIONS16 (SP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/*
|
||||
* Manufacturer ID list
|
||||
*/
|
||||
* The chip ID list:
|
||||
* name, device ID, page size, chip size in MiB, eraseblock size, options
|
||||
*
|
||||
* If page size and eraseblock size are 0, the sizes are taken from the
|
||||
* extended chip ID.
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
/*
|
||||
* Some incompatible NAND chips share device ID's and so must be
|
||||
* listed by full ID. We list them first so that we can easily identify
|
||||
* the most specific match.
|
||||
*/
|
||||
{"TC58NVG2S0F 4G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} },
|
||||
SZ_4K, SZ_512, SZ_256K, 0, 8, 224},
|
||||
{"TC58NVG3S0F 8G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} },
|
||||
SZ_4K, SZ_1K, SZ_256K, 0, 8, 232},
|
||||
{"TC58NVG5D2 32G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} },
|
||||
SZ_8K, SZ_4K, SZ_1M, 0, 8, 640},
|
||||
{"TC58NVG6D2 64G 3.3V 8-bit",
|
||||
{ .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
|
||||
SZ_8K, SZ_8K, SZ_2M, 0, 8, 640},
|
||||
|
||||
LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE5, 4, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xD6, 8, SZ_8K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 8MiB 3,3V 8-bit", 0xE6, 8, SZ_8K, SP_OPTIONS),
|
||||
|
||||
LEGACY_ID_NAND("NAND 16MiB 1,8V 8-bit", 0x33, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 16MiB 3,3V 8-bit", 0x73, 16, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 16MiB 1,8V 16-bit", 0x43, 16, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 16MiB 3,3V 16-bit", 0x53, 16, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 32MiB 1,8V 8-bit", 0x35, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 32MiB 3,3V 8-bit", 0x75, 32, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 32MiB 1,8V 16-bit", 0x45, 32, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 32MiB 3,3V 16-bit", 0x55, 32, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 64MiB 1,8V 8-bit", 0x36, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 64MiB 3,3V 8-bit", 0x76, 64, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 64MiB 1,8V 16-bit", 0x46, 64, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 64MiB 3,3V 16-bit", 0x56, 64, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x78, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 8-bit", 0x39, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 8-bit", 0x79, 128, SZ_16K, SP_OPTIONS),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x72, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 1,8V 16-bit", 0x49, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x74, 128, SZ_16K, SP_OPTIONS16),
|
||||
LEGACY_ID_NAND("NAND 128MiB 3,3V 16-bit", 0x59, 128, SZ_16K, SP_OPTIONS16),
|
||||
|
||||
LEGACY_ID_NAND("NAND 256MiB 3,3V 8-bit", 0x71, 256, SZ_16K, SP_OPTIONS),
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. Their page size and
|
||||
* eraseblock size are determined from the extended ID bytes.
|
||||
*/
|
||||
|
||||
/* 512 Megabit */
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 8-bit", 0xA0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF2, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xD0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 8-bit", 0xF0, 64, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 1,8V 16-bit", 0xB0, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC2, 64, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64MiB 3,3V 16-bit", 0xC0, 64, LP_OPTIONS16),
|
||||
|
||||
/* 1 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 8-bit", 0xA1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xF1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 8-bit", 0xD1, 128, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xB1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 3,3V 16-bit", 0xC1, 128, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 128MiB 1,8V 16-bit", 0xAD, 128, LP_OPTIONS16),
|
||||
|
||||
/* 2 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 256MiB 1,8V 8-bit", 0xAA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 3,3V 8-bit", 0xDA, 256, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 1,8V 16-bit", 0xBA, 256, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 256MiB 3,3V 16-bit", 0xCA, 256, LP_OPTIONS16),
|
||||
|
||||
/* 4 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 512MiB 1,8V 8-bit", 0xAC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 3,3V 8-bit", 0xDC, 512, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 1,8V 16-bit", 0xBC, 512, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 512MiB 3,3V 16-bit", 0xCC, 512, LP_OPTIONS16),
|
||||
|
||||
/* 8 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 1GiB 1,8V 8-bit", 0xA3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 3,3V 8-bit", 0xD3, 1024, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 1,8V 16-bit", 0xB3, 1024, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 1GiB 3,3V 16-bit", 0xC3, 1024, LP_OPTIONS16),
|
||||
|
||||
/* 16 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 2GiB 1,8V 8-bit", 0xA5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 3,3V 8-bit", 0xD5, 2048, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 1,8V 16-bit", 0xB5, 2048, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 2GiB 3,3V 16-bit", 0xC5, 2048, LP_OPTIONS16),
|
||||
|
||||
/* 32 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 4GiB 1,8V 8-bit", 0xA7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 3,3V 8-bit", 0xD7, 4096, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 1,8V 16-bit", 0xB7, 4096, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 4GiB 3,3V 16-bit", 0xC7, 4096, LP_OPTIONS16),
|
||||
|
||||
/* 64 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 8GiB 1,8V 8-bit", 0xAE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 3,3V 8-bit", 0xDE, 8192, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 1,8V 16-bit", 0xBE, 8192, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 8GiB 3,3V 16-bit", 0xCE, 8192, LP_OPTIONS16),
|
||||
|
||||
/* 128 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 16GiB 1,8V 8-bit", 0x1A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 3,3V 8-bit", 0x3A, 16384, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 1,8V 16-bit", 0x2A, 16384, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 16GiB 3,3V 16-bit", 0x4A, 16384, LP_OPTIONS16),
|
||||
|
||||
/* 256 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 32GiB 1,8V 8-bit", 0x1C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 3,3V 8-bit", 0x3C, 32768, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 1,8V 16-bit", 0x2C, 32768, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 32GiB 3,3V 16-bit", 0x4C, 32768, LP_OPTIONS16),
|
||||
|
||||
/* 512 Gigabit */
|
||||
EXTENDED_ID_NAND("NAND 64GiB 1,8V 8-bit", 0x1E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 3,3V 8-bit", 0x3E, 65536, LP_OPTIONS),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 1,8V 16-bit", 0x2E, 65536, LP_OPTIONS16),
|
||||
EXTENDED_ID_NAND("NAND 64GiB 3,3V 16-bit", 0x4E, 65536, LP_OPTIONS16),
|
||||
|
||||
{NULL}
|
||||
};
|
||||
|
||||
/* Manufacturer IDs */
|
||||
struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{NAND_MFR_TOSHIBA, "Toshiba"},
|
||||
{NAND_MFR_SAMSUNG, "Samsung"},
|
||||
|
|
|
@ -218,7 +218,6 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
|
|||
#define STATE_CMD_READOOB 0x00000005 /* read OOB area */
|
||||
#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */
|
||||
#define STATE_CMD_STATUS 0x00000007 /* read status */
|
||||
#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */
|
||||
#define STATE_CMD_SEQIN 0x00000009 /* sequential data input */
|
||||
#define STATE_CMD_READID 0x0000000A /* read ID */
|
||||
#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */
|
||||
|
@ -263,14 +262,13 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should "
|
|||
#define NS_OPER_STATES 6 /* Maximum number of states in operation */
|
||||
|
||||
#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */
|
||||
#define OPT_PAGE256 0x00000001 /* 256-byte page chips */
|
||||
#define OPT_PAGE512 0x00000002 /* 512-byte page chips */
|
||||
#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */
|
||||
#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */
|
||||
#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
|
||||
#define OPT_PAGE4096 0x00000080 /* 4096-byte page chips */
|
||||
#define OPT_LARGEPAGE (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
|
||||
#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */
|
||||
#define OPT_SMALLPAGE (OPT_PAGE512) /* 512-byte page chips */
|
||||
|
||||
/* Remove action bits from state */
|
||||
#define NS_STATE(x) ((x) & ~ACTION_MASK)
|
||||
|
@ -406,8 +404,6 @@ static struct nandsim_operations {
|
|||
{OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}},
|
||||
/* Read status */
|
||||
{OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}},
|
||||
/* Read multi-plane status */
|
||||
{OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}},
|
||||
/* Read ID */
|
||||
{OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}},
|
||||
/* Large page devices read page */
|
||||
|
@ -699,10 +695,7 @@ static int init_nandsim(struct mtd_info *mtd)
|
|||
ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec;
|
||||
ns->options = 0;
|
||||
|
||||
if (ns->geom.pgsz == 256) {
|
||||
ns->options |= OPT_PAGE256;
|
||||
}
|
||||
else if (ns->geom.pgsz == 512) {
|
||||
if (ns->geom.pgsz == 512) {
|
||||
ns->options |= OPT_PAGE512;
|
||||
if (ns->busw == 8)
|
||||
ns->options |= OPT_PAGE512_8BIT;
|
||||
|
@ -769,9 +762,9 @@ static int init_nandsim(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
/* Detect how many ID bytes the NAND chip outputs */
|
||||
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
||||
if (second_id_byte != nand_flash_ids[i].id)
|
||||
continue;
|
||||
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
||||
if (second_id_byte != nand_flash_ids[i].dev_id)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ns->busw == 16)
|
||||
|
@ -1079,8 +1072,6 @@ static char *get_state_name(uint32_t state)
|
|||
return "STATE_CMD_ERASE1";
|
||||
case STATE_CMD_STATUS:
|
||||
return "STATE_CMD_STATUS";
|
||||
case STATE_CMD_STATUS_M:
|
||||
return "STATE_CMD_STATUS_M";
|
||||
case STATE_CMD_SEQIN:
|
||||
return "STATE_CMD_SEQIN";
|
||||
case STATE_CMD_READID:
|
||||
|
@ -1145,7 +1136,6 @@ static int check_command(int cmd)
|
|||
case NAND_CMD_RNDOUTSTART:
|
||||
return 0;
|
||||
|
||||
case NAND_CMD_STATUS_MULTI:
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
@ -1171,8 +1161,6 @@ static uint32_t get_state_by_command(unsigned command)
|
|||
return STATE_CMD_ERASE1;
|
||||
case NAND_CMD_STATUS:
|
||||
return STATE_CMD_STATUS;
|
||||
case NAND_CMD_STATUS_MULTI:
|
||||
return STATE_CMD_STATUS_M;
|
||||
case NAND_CMD_SEQIN:
|
||||
return STATE_CMD_SEQIN;
|
||||
case NAND_CMD_READID:
|
||||
|
@ -2306,7 +2294,7 @@ static int __init ns_init_module(void)
|
|||
nand->geom.idbytes = 2;
|
||||
nand->regs.status = NS_STATUS_OK(nand);
|
||||
nand->nxstate = STATE_UNKNOWN;
|
||||
nand->options |= OPT_PAGE256; /* temporary value */
|
||||
nand->options |= OPT_PAGE512; /* temporary value */
|
||||
nand->ids[0] = first_id_byte;
|
||||
nand->ids[1] = second_id_byte;
|
||||
nand->ids[2] = third_id_byte;
|
||||
|
|
|
@ -177,15 +177,6 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
|
|||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_RNDIN:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_DEPLETE1:
|
||||
return;
|
||||
|
||||
case NAND_CMD_STATUS_ERROR:
|
||||
case NAND_CMD_STATUS_ERROR0:
|
||||
case NAND_CMD_STATUS_ERROR1:
|
||||
case NAND_CMD_STATUS_ERROR2:
|
||||
case NAND_CMD_STATUS_ERROR3:
|
||||
udelay(chip->chip_delay);
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
|
|
|
@ -1023,9 +1023,9 @@ static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
|
|||
int status, state = this->state;
|
||||
|
||||
if (state == FL_ERASING)
|
||||
timeo += (HZ * 400) / 1000;
|
||||
timeo += msecs_to_jiffies(400);
|
||||
else
|
||||
timeo += (HZ * 20) / 1000;
|
||||
timeo += msecs_to_jiffies(20);
|
||||
|
||||
writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command);
|
||||
while (time_before(jiffies, timeo)) {
|
||||
|
@ -1701,8 +1701,9 @@ static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt)
|
|||
elm_node = of_find_node_by_phandle(be32_to_cpup(parp));
|
||||
pdev = of_find_device_by_node(elm_node);
|
||||
info->elm_dev = &pdev->dev;
|
||||
elm_config(info->elm_dev, bch_type);
|
||||
info->is_elm_used = true;
|
||||
|
||||
if (elm_config(info->elm_dev, bch_type) == 0)
|
||||
info->is_elm_used = true;
|
||||
}
|
||||
|
||||
if (info->is_elm_used && (mtd->writesize <= 4096)) {
|
||||
|
|
|
@ -231,18 +231,7 @@ static struct platform_driver orion_nand_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init orion_nand_init(void)
|
||||
{
|
||||
return platform_driver_probe(&orion_nand_driver, orion_nand_probe);
|
||||
}
|
||||
|
||||
static void __exit orion_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&orion_nand_driver);
|
||||
}
|
||||
|
||||
module_init(orion_nand_init);
|
||||
module_exit(orion_nand_exit);
|
||||
module_platform_driver_probe(orion_nand_driver, orion_nand_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Tzachi Perelstein");
|
||||
|
|
|
@ -1,403 +0,0 @@
|
|||
/*
|
||||
* drivers/mtd/nand/ppchameleonevb.c
|
||||
*
|
||||
* Copyright (C) 2003 DAVE Srl (info@wawnet.biz)
|
||||
*
|
||||
* Derived from drivers/mtd/nand/edb7312.c
|
||||
*
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash devices found on the
|
||||
* PPChameleon/PPChameleonEVB system.
|
||||
* PPChameleon options (autodetected):
|
||||
* - BA model: no NAND
|
||||
* - ME model: 32MB (Samsung K9F5608U0B)
|
||||
* - HI model: 128MB (Samsung K9F1G08UOM)
|
||||
* PPChameleonEVB options:
|
||||
* - 32MB (Samsung K9F5608U0B)
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <platforms/PPChameleonEVB.h>
|
||||
|
||||
#undef USE_READY_BUSY_PIN
|
||||
#define USE_READY_BUSY_PIN
|
||||
/* see datasheets (tR) */
|
||||
#define NAND_BIG_DELAY_US 25
|
||||
#define NAND_SMALL_DELAY_US 10
|
||||
|
||||
/* handy sizes */
|
||||
#define SZ_4M 0x00400000
|
||||
#define NAND_SMALL_SIZE 0x02000000
|
||||
#define NAND_MTD_NAME "ppchameleon-nand"
|
||||
#define NAND_EVB_MTD_NAME "ppchameleonevb-nand"
|
||||
|
||||
/* GPIO pins used to drive NAND chip mounted on processor module */
|
||||
#define NAND_nCE_GPIO_PIN (0x80000000 >> 1)
|
||||
#define NAND_CLE_GPIO_PIN (0x80000000 >> 2)
|
||||
#define NAND_ALE_GPIO_PIN (0x80000000 >> 3)
|
||||
#define NAND_RB_GPIO_PIN (0x80000000 >> 4)
|
||||
/* GPIO pins used to drive NAND chip mounted on EVB */
|
||||
#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14)
|
||||
#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15)
|
||||
#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16)
|
||||
#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31)
|
||||
|
||||
/*
|
||||
* MTD structure for PPChameleonEVB board
|
||||
*/
|
||||
static struct mtd_info *ppchameleon_mtd = NULL;
|
||||
static struct mtd_info *ppchameleonevb_mtd = NULL;
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR;
|
||||
static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
|
||||
|
||||
#ifdef MODULE
|
||||
module_param(ppchameleon_fio_pbase, ulong, 0);
|
||||
module_param(ppchameleonevb_fio_pbase, ulong, 0);
|
||||
#else
|
||||
__setup("ppchameleon_fio_pbase=", ppchameleon_fio_pbase);
|
||||
__setup("ppchameleonevb_fio_pbase=", ppchameleonevb_fio_pbase);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Define static partitions for flash devices
|
||||
*/
|
||||
static struct mtd_partition partition_info_hi[] = {
|
||||
{ .name = "PPChameleon HI Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 128 * 1024 * 1024
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info_me[] = {
|
||||
{ .name = "PPChameleon ME Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 32 * 1024 * 1024
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info_evb[] = {
|
||||
{ .name = "PPChameleonEVB Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 32 * 1024 * 1024
|
||||
}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
#error Missing headerfiles. No way to fix this. -tglx
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETCLE:
|
||||
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
#error Missing headerfiles. No way to fix this. -tglx
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETCLE:
|
||||
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
/*
|
||||
* read device ready pin
|
||||
*/
|
||||
static int ppchameleon_device_ready(struct mtd_info *minfo)
|
||||
{
|
||||
if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_RB_GPIO_PIN)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppchameleonevb_device_ready(struct mtd_info *minfo)
|
||||
{
|
||||
if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init ppchameleonevb_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
void __iomem *ppchameleon_fio_base;
|
||||
void __iomem *ppchameleonevb_fio_base;
|
||||
|
||||
/*********************************
|
||||
* Processor module NAND (if any) *
|
||||
*********************************/
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ppchameleon_mtd) {
|
||||
printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* map physical address */
|
||||
ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M);
|
||||
if (!ppchameleon_fio_base) {
|
||||
printk("ioremap PPChameleon NAND flash failed\n");
|
||||
kfree(ppchameleon_mtd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *)(&ppchameleon_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(ppchameleon_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ppchameleon_mtd->priv = this;
|
||||
ppchameleon_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Initialize GPIOs */
|
||||
/* Pin mapping for NAND chip */
|
||||
/*
|
||||
CE GPIO_01
|
||||
CLE GPIO_02
|
||||
ALE GPIO_03
|
||||
R/B GPIO_04
|
||||
*/
|
||||
/* output select */
|
||||
out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xC0FFFFFF);
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xC0FFFFFF);
|
||||
/* enable output driver */
|
||||
out_be32((volatile unsigned *)GPIO0_TCR,
|
||||
in_be32((volatile unsigned *)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFF3FFFFF);
|
||||
/* high-impedecence */
|
||||
out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
|
||||
/* input select */
|
||||
out_be32((volatile unsigned *)GPIO0_ISR1H,
|
||||
(in_be32((volatile unsigned *)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
|
||||
#endif
|
||||
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = ppchameleon_fio_base;
|
||||
this->IO_ADDR_W = ppchameleon_fio_base;
|
||||
this->cmd_ctrl = ppchameleon_hwcontrol;
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
this->dev_ready = ppchameleon_device_ready;
|
||||
#endif
|
||||
this->chip_delay = NAND_BIG_DELAY_US;
|
||||
/* ECC mode */
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Scan to find existence of the device (it could not be mounted) */
|
||||
if (nand_scan(ppchameleon_mtd, 1)) {
|
||||
iounmap((void *)ppchameleon_fio_base);
|
||||
ppchameleon_fio_base = NULL;
|
||||
kfree(ppchameleon_mtd);
|
||||
goto nand_evb_init;
|
||||
}
|
||||
#ifndef USE_READY_BUSY_PIN
|
||||
/* Adjust delay if necessary */
|
||||
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
|
||||
this->chip_delay = NAND_SMALL_DELAY_US;
|
||||
#endif
|
||||
|
||||
ppchameleon_mtd->name = "ppchameleon-nand";
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(ppchameleon_mtd, NULL, NULL,
|
||||
ppchameleon_mtd->size == NAND_SMALL_SIZE ?
|
||||
partition_info_me : partition_info_hi,
|
||||
NUM_PARTITIONS);
|
||||
|
||||
nand_evb_init:
|
||||
/****************************
|
||||
* EVB NAND (always present) *
|
||||
****************************/
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ppchameleonevb_mtd) {
|
||||
printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
|
||||
if (ppchameleon_fio_base)
|
||||
iounmap(ppchameleon_fio_base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* map physical address */
|
||||
ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M);
|
||||
if (!ppchameleonevb_fio_base) {
|
||||
printk("ioremap PPChameleonEVB NAND flash failed\n");
|
||||
kfree(ppchameleonevb_mtd);
|
||||
if (ppchameleon_fio_base)
|
||||
iounmap(ppchameleon_fio_base);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *)(&ppchameleonevb_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ppchameleonevb_mtd->priv = this;
|
||||
|
||||
/* Initialize GPIOs */
|
||||
/* Pin mapping for NAND chip */
|
||||
/*
|
||||
CE GPIO_14
|
||||
CLE GPIO_15
|
||||
ALE GPIO_16
|
||||
R/B GPIO_31
|
||||
*/
|
||||
/* output select */
|
||||
out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xFFFFFFF0);
|
||||
out_be32((volatile unsigned *)GPIO0_OSRL, in_be32((volatile unsigned *)GPIO0_OSRL) & 0x3FFFFFFF);
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFFFFFFF0);
|
||||
out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0x3FFFFFFF);
|
||||
/* enable output driver */
|
||||
out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
|
||||
NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0xFFFFFFFC);
|
||||
/* high-impedecence */
|
||||
out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
|
||||
/* input select */
|
||||
out_be32((volatile unsigned *)GPIO0_ISR1L,
|
||||
(in_be32((volatile unsigned *)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
|
||||
#endif
|
||||
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = ppchameleonevb_fio_base;
|
||||
this->IO_ADDR_W = ppchameleonevb_fio_base;
|
||||
this->cmd_ctrl = ppchameleonevb_hwcontrol;
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
this->dev_ready = ppchameleonevb_device_ready;
|
||||
#endif
|
||||
this->chip_delay = NAND_SMALL_DELAY_US;
|
||||
|
||||
/* ECC mode */
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(ppchameleonevb_mtd, 1)) {
|
||||
iounmap((void *)ppchameleonevb_fio_base);
|
||||
kfree(ppchameleonevb_mtd);
|
||||
if (ppchameleon_fio_base)
|
||||
iounmap(ppchameleon_fio_base);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
|
||||
|
||||
/* Register the partitions */
|
||||
mtd_device_parse_register(ppchameleonevb_mtd, NULL, NULL,
|
||||
ppchameleon_mtd->size == NAND_SMALL_SIZE ?
|
||||
partition_info_me : partition_info_hi,
|
||||
NUM_PARTITIONS);
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(ppchameleonevb_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit ppchameleonevb_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
|
||||
/* Release resources, unregister device(s) */
|
||||
nand_release(ppchameleon_mtd);
|
||||
nand_release(ppchameleonevb_mtd);
|
||||
|
||||
/* Release iomaps */
|
||||
this = (struct nand_chip *) &ppchameleon_mtd[1];
|
||||
iounmap((void *) this->IO_ADDR_R);
|
||||
this = (struct nand_chip *) &ppchameleonevb_mtd[1];
|
||||
iounmap((void *) this->IO_ADDR_R);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (ppchameleon_mtd);
|
||||
kfree (ppchameleonevb_mtd);
|
||||
}
|
||||
module_exit(ppchameleonevb_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("DAVE Srl <support-ppchameleon@dave-tech.it>");
|
||||
MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board");
|
|
@ -989,7 +989,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)
|
|||
}
|
||||
|
||||
pxa3xx_flash_ids[0].name = f->name;
|
||||
pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;
|
||||
pxa3xx_flash_ids[0].dev_id = (f->chip_id >> 8) & 0xffff;
|
||||
pxa3xx_flash_ids[0].pagesize = f->page_size;
|
||||
chipsize = (uint64_t)f->num_blocks * f->page_per_block * f->page_size;
|
||||
pxa3xx_flash_ids[0].chipsize = chipsize >> 20;
|
||||
|
|
|
@ -1,624 +0,0 @@
|
|||
/*
|
||||
* drivers/mtd/nand/rtc_from4.c
|
||||
*
|
||||
* Copyright (C) 2004 Red Hat, Inc.
|
||||
*
|
||||
* Derived from drivers/mtd/nand/spia.c
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the AG-AND flash device found on the
|
||||
* Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4),
|
||||
* which utilizes the Renesas HN29V1G91T-30 part.
|
||||
* This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rslib.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* MTD structure for Renesas board
|
||||
*/
|
||||
static struct mtd_info *rtc_from4_mtd = NULL;
|
||||
|
||||
#define RTC_FROM4_MAX_CHIPS 2
|
||||
|
||||
/* HS77x9 processor register defines */
|
||||
#define SH77X9_BCR1 ((volatile unsigned short *)(0xFFFFFF60))
|
||||
#define SH77X9_BCR2 ((volatile unsigned short *)(0xFFFFFF62))
|
||||
#define SH77X9_WCR1 ((volatile unsigned short *)(0xFFFFFF64))
|
||||
#define SH77X9_WCR2 ((volatile unsigned short *)(0xFFFFFF66))
|
||||
#define SH77X9_MCR ((volatile unsigned short *)(0xFFFFFF68))
|
||||
#define SH77X9_PCR ((volatile unsigned short *)(0xFFFFFF6C))
|
||||
#define SH77X9_FRQCR ((volatile unsigned short *)(0xFFFFFF80))
|
||||
|
||||
/*
|
||||
* Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor)
|
||||
*/
|
||||
/* Address where flash is mapped */
|
||||
#define RTC_FROM4_FIO_BASE 0x14000000
|
||||
|
||||
/* CLE and ALE are tied to address lines 5 & 4, respectively */
|
||||
#define RTC_FROM4_CLE (1 << 5)
|
||||
#define RTC_FROM4_ALE (1 << 4)
|
||||
|
||||
/* address lines A24-A22 used for chip selection */
|
||||
#define RTC_FROM4_NAND_ADDR_SLOT3 (0x00800000)
|
||||
#define RTC_FROM4_NAND_ADDR_SLOT4 (0x00C00000)
|
||||
#define RTC_FROM4_NAND_ADDR_FPGA (0x01000000)
|
||||
/* mask address lines A24-A22 used for chip selection */
|
||||
#define RTC_FROM4_NAND_ADDR_MASK (RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA)
|
||||
|
||||
/* FPGA status register for checking device ready (bit zero) */
|
||||
#define RTC_FROM4_FPGA_SR (RTC_FROM4_NAND_ADDR_FPGA | 0x00000002)
|
||||
#define RTC_FROM4_DEVICE_READY 0x0001
|
||||
|
||||
/* FPGA Reed-Solomon ECC Control register */
|
||||
|
||||
#define RTC_FROM4_RS_ECC_CTL (RTC_FROM4_NAND_ADDR_FPGA | 0x00000050)
|
||||
#define RTC_FROM4_RS_ECC_CTL_CLR (1 << 7)
|
||||
#define RTC_FROM4_RS_ECC_CTL_GEN (1 << 6)
|
||||
#define RTC_FROM4_RS_ECC_CTL_FD_E (1 << 5)
|
||||
|
||||
/* FPGA Reed-Solomon ECC code base */
|
||||
#define RTC_FROM4_RS_ECC (RTC_FROM4_NAND_ADDR_FPGA | 0x00000060)
|
||||
#define RTC_FROM4_RS_ECCN (RTC_FROM4_NAND_ADDR_FPGA | 0x00000080)
|
||||
|
||||
/* FPGA Reed-Solomon ECC check register */
|
||||
#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
|
||||
#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7)
|
||||
|
||||
#define ERR_STAT_ECC_AVAILABLE 0x20
|
||||
|
||||
/* Undefine for software ECC */
|
||||
#define RTC_FROM4_HWECC 1
|
||||
|
||||
/* Define as 1 for no virtual erase blocks (in JFFS2) */
|
||||
#define RTC_FROM4_NO_VIRTBLOCKS 0
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE);
|
||||
|
||||
static const struct mtd_partition partition_info[] = {
|
||||
{
|
||||
.name = "Renesas flash partition 1",
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL},
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
/*
|
||||
* hardware specific flash bbt decriptors
|
||||
* Note: this is to allow debugging by disabling
|
||||
* NAND_BBT_CREATE and/or NAND_BBT_WRITE
|
||||
*
|
||||
*/
|
||||
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
|
||||
static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
|
||||
|
||||
static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
||||
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
|
||||
.offs = 40,
|
||||
.len = 4,
|
||||
.veroffs = 44,
|
||||
.maxblocks = 4,
|
||||
.pattern = bbt_pattern
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
||||
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
|
||||
.offs = 40,
|
||||
.len = 4,
|
||||
.veroffs = 44,
|
||||
.maxblocks = 4,
|
||||
.pattern = mirror_pattern
|
||||
};
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
|
||||
/* the Reed Solomon control structure */
|
||||
static struct rs_control *rs_decoder;
|
||||
|
||||
/*
|
||||
* hardware specific Out Of Band information
|
||||
*/
|
||||
static struct nand_ecclayout rtc_from4_nand_oobinfo = {
|
||||
.eccbytes = 32,
|
||||
.eccpos = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31},
|
||||
.oobfree = {{32, 32}}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* rtc_from4_hwcontrol - hardware specific access to control-lines
|
||||
* @mtd: MTD device structure
|
||||
* @cmd: hardware control command
|
||||
*
|
||||
* Address lines (A5 and A4) are used to control Command and Address Latch
|
||||
* Enable on this board, so set the read/write address appropriately.
|
||||
*
|
||||
* Chip Enable is also controlled by the Chip Select (CS5) and
|
||||
* Address lines (A24-A22), so no action is required here.
|
||||
*
|
||||
*/
|
||||
static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = (mtd->priv);
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_CLE);
|
||||
else
|
||||
writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_ALE);
|
||||
}
|
||||
|
||||
/*
|
||||
* rtc_from4_nand_select_chip - hardware specific chip select
|
||||
* @mtd: MTD device structure
|
||||
* @chip: Chip to select (0 == slot 3, 1 == slot 4)
|
||||
*
|
||||
* The chip select is based on address lines A24-A22.
|
||||
* This driver uses flash slots 3 and 4 (A23-A22).
|
||||
*
|
||||
*/
|
||||
static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
|
||||
|
||||
switch (chip) {
|
||||
|
||||
case 0: /* select slot 3 chip */
|
||||
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
|
||||
break;
|
||||
case 1: /* select slot 4 chip */
|
||||
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rtc_from4_nand_device_ready - hardware specific ready/busy check
|
||||
* @mtd: MTD device structure
|
||||
*
|
||||
* This board provides the Ready/Busy state in the status register
|
||||
* of the FPGA. Bit zero indicates the RDY(1)/BSY(0) signal.
|
||||
*
|
||||
*/
|
||||
static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
unsigned short status;
|
||||
|
||||
status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR));
|
||||
|
||||
return (status & RTC_FROM4_DEVICE_READY);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* deplete - code to perform device recovery in case there was a power loss
|
||||
* @mtd: MTD device structure
|
||||
* @chip: Chip to select (0 == slot 3, 1 == slot 4)
|
||||
*
|
||||
* If there was a sudden loss of power during an erase operation, a
|
||||
* "device recovery" operation must be performed when power is restored
|
||||
* to ensure correct operation. This routine performs the required steps
|
||||
* for the requested chip.
|
||||
*
|
||||
* See page 86 of the data sheet for details.
|
||||
*
|
||||
*/
|
||||
static void deplete(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* wait until device is ready */
|
||||
while (!this->dev_ready(mtd)) ;
|
||||
|
||||
this->select_chip(mtd, chip);
|
||||
|
||||
/* Send the commands for device recovery, phase 1 */
|
||||
this->cmdfunc(mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000);
|
||||
this->cmdfunc(mtd, NAND_CMD_DEPLETE2, -1, -1);
|
||||
|
||||
/* Send the commands for device recovery, phase 2 */
|
||||
this->cmdfunc(mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004);
|
||||
this->cmdfunc(mtd, NAND_CMD_DEPLETE2, -1, -1);
|
||||
|
||||
}
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
/*
|
||||
* rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
|
||||
* @mtd: MTD device structure
|
||||
* @mode: I/O mode; read or write
|
||||
*
|
||||
* enable hardware ECC for data read or write
|
||||
*
|
||||
*/
|
||||
static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
volatile unsigned short *rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
|
||||
unsigned short status;
|
||||
|
||||
switch (mode) {
|
||||
case NAND_ECC_READ:
|
||||
status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_FD_E;
|
||||
|
||||
*rs_ecc_ctl = status;
|
||||
break;
|
||||
|
||||
case NAND_ECC_READSYN:
|
||||
status = 0x00;
|
||||
|
||||
*rs_ecc_ctl = status;
|
||||
break;
|
||||
|
||||
case NAND_ECC_WRITE:
|
||||
status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_GEN | RTC_FROM4_RS_ECC_CTL_FD_E;
|
||||
|
||||
*rs_ecc_ctl = status;
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* rtc_from4_calculate_ecc - hardware specific code to read ECC code
|
||||
* @mtd: MTD device structure
|
||||
* @dat: buffer containing the data to generate ECC codes
|
||||
* @ecc_code ECC codes calculated
|
||||
*
|
||||
* The ECC code is calculated by the FPGA. All we have to do is read the values
|
||||
* from the FPGA registers.
|
||||
*
|
||||
* Note: We read from the inverted registers, since data is inverted before
|
||||
* the code is calculated. So all 0xff data (blank page) results in all 0xff rs code
|
||||
*
|
||||
*/
|
||||
static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
volatile unsigned short *rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
|
||||
unsigned short value;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
value = *rs_eccn;
|
||||
ecc_code[i] = (unsigned char)value;
|
||||
rs_eccn++;
|
||||
}
|
||||
ecc_code[7] |= 0x0f; /* set the last four bits (not used) */
|
||||
}
|
||||
|
||||
/*
|
||||
* rtc_from4_correct_data - hardware specific code to correct data using ECC code
|
||||
* @mtd: MTD device structure
|
||||
* @buf: buffer containing the data to generate ECC codes
|
||||
* @ecc1 ECC codes read
|
||||
* @ecc2 ECC codes calculated
|
||||
*
|
||||
* The FPGA tells us fast, if there's an error or not. If no, we go back happy
|
||||
* else we read the ecc results from the fpga and call the rs library to decode
|
||||
* and hopefully correct the error.
|
||||
*
|
||||
*/
|
||||
static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2)
|
||||
{
|
||||
int i, j, res;
|
||||
unsigned short status;
|
||||
uint16_t par[6], syn[6];
|
||||
uint8_t ecc[8];
|
||||
volatile unsigned short *rs_ecc;
|
||||
|
||||
status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
|
||||
|
||||
if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read the syndrome pattern from the FPGA and correct the bitorder */
|
||||
rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
|
||||
for (i = 0; i < 8; i++) {
|
||||
ecc[i] = bitrev8(*rs_ecc);
|
||||
rs_ecc++;
|
||||
}
|
||||
|
||||
/* convert into 6 10bit syndrome fields */
|
||||
par[5] = rs_decoder->index_of[(((uint16_t) ecc[0] >> 0) & 0x0ff) | (((uint16_t) ecc[1] << 8) & 0x300)];
|
||||
par[4] = rs_decoder->index_of[(((uint16_t) ecc[1] >> 2) & 0x03f) | (((uint16_t) ecc[2] << 6) & 0x3c0)];
|
||||
par[3] = rs_decoder->index_of[(((uint16_t) ecc[2] >> 4) & 0x00f) | (((uint16_t) ecc[3] << 4) & 0x3f0)];
|
||||
par[2] = rs_decoder->index_of[(((uint16_t) ecc[3] >> 6) & 0x003) | (((uint16_t) ecc[4] << 2) & 0x3fc)];
|
||||
par[1] = rs_decoder->index_of[(((uint16_t) ecc[5] >> 0) & 0x0ff) | (((uint16_t) ecc[6] << 8) & 0x300)];
|
||||
par[0] = (((uint16_t) ecc[6] >> 2) & 0x03f) | (((uint16_t) ecc[7] << 6) & 0x3c0);
|
||||
|
||||
/* Convert to computable syndrome */
|
||||
for (i = 0; i < 6; i++) {
|
||||
syn[i] = par[0];
|
||||
for (j = 1; j < 6; j++)
|
||||
if (par[j] != rs_decoder->nn)
|
||||
syn[i] ^= rs_decoder->alpha_to[rs_modnn(rs_decoder, par[j] + i * j)];
|
||||
|
||||
/* Convert to index form */
|
||||
syn[i] = rs_decoder->index_of[syn[i]];
|
||||
}
|
||||
|
||||
/* Let the library code do its magic. */
|
||||
res = decode_rs8(rs_decoder, (uint8_t *) buf, par, 512, syn, 0, NULL, 0xff, NULL);
|
||||
if (res > 0) {
|
||||
pr_debug("rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* rtc_from4_errstat - perform additional error status checks
|
||||
* @mtd: MTD device structure
|
||||
* @this: NAND chip structure
|
||||
* @state: state or the operation
|
||||
* @status: status code returned from read status
|
||||
* @page: startpage inside the chip, must be called with (page & this->pagemask)
|
||||
*
|
||||
* Perform additional error status checks on erase and write failures
|
||||
* to determine if errors are correctable. For this device, correctable
|
||||
* 1-bit errors on erase and write are considered acceptable.
|
||||
*
|
||||
* note: see pages 34..37 of data sheet for details.
|
||||
*
|
||||
*/
|
||||
static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this,
|
||||
int state, int status, int page)
|
||||
{
|
||||
int er_stat = 0;
|
||||
int rtn, retlen;
|
||||
size_t len;
|
||||
uint8_t *buf;
|
||||
int i;
|
||||
|
||||
this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
|
||||
|
||||
if (state == FL_ERASING) {
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!(status & 1 << (i + 1)))
|
||||
continue;
|
||||
this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1),
|
||||
-1, -1);
|
||||
rtn = this->read_byte(mtd);
|
||||
this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
|
||||
|
||||
/* err_ecc_not_avail */
|
||||
if (!(rtn & ERR_STAT_ECC_AVAILABLE))
|
||||
er_stat |= 1 << (i + 1);
|
||||
}
|
||||
|
||||
} else if (state == FL_WRITING) {
|
||||
|
||||
unsigned long corrected = mtd->ecc_stats.corrected;
|
||||
|
||||
/* single bank write logic */
|
||||
this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1);
|
||||
rtn = this->read_byte(mtd);
|
||||
this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
|
||||
|
||||
if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
|
||||
/* err_ecc_not_avail */
|
||||
er_stat |= 1 << 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = mtd->writesize;
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
er_stat = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* recovery read */
|
||||
rtn = nand_do_read(mtd, page, len, &retlen, buf);
|
||||
|
||||
/* if read failed or > 1-bit error corrected */
|
||||
if (rtn || (mtd->ecc_stats.corrected - corrected) > 1)
|
||||
er_stat |= 1 << 1;
|
||||
kfree(buf);
|
||||
}
|
||||
out:
|
||||
rtn = status;
|
||||
if (er_stat == 0) { /* if ECC is available */
|
||||
rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init rtc_from4_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
unsigned short bcr1, bcr2, wcr2;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!rtc_from4_mtd) {
|
||||
printk("Unable to allocate Renesas NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *)(&rtc_from4_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(rtc_from4_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
rtc_from4_mtd->priv = this;
|
||||
rtc_from4_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
|
||||
bcr1 = *SH77X9_BCR1 & ~0x0002;
|
||||
bcr1 |= 0x0002;
|
||||
*SH77X9_BCR1 = bcr1;
|
||||
|
||||
/* set */
|
||||
bcr2 = *SH77X9_BCR2 & ~0x0c00;
|
||||
bcr2 |= 0x0800;
|
||||
*SH77X9_BCR2 = bcr2;
|
||||
|
||||
/* set area 5 wait states */
|
||||
wcr2 = *SH77X9_WCR2 & ~0x1c00;
|
||||
wcr2 |= 0x1c00;
|
||||
*SH77X9_WCR2 = wcr2;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = rtc_from4_fio_base;
|
||||
this->IO_ADDR_W = rtc_from4_fio_base;
|
||||
/* Set address of hardware control function */
|
||||
this->cmd_ctrl = rtc_from4_hwcontrol;
|
||||
/* Set address of chip select function */
|
||||
this->select_chip = rtc_from4_nand_select_chip;
|
||||
/* command delay time (in us) */
|
||||
this->chip_delay = 100;
|
||||
/* return the status of the Ready/Busy line */
|
||||
this->dev_ready = rtc_from4_nand_device_ready;
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
|
||||
|
||||
this->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 8;
|
||||
this->ecc.strength = 3;
|
||||
/* return the status of extra status and ECC checks */
|
||||
this->errstat = rtc_from4_errstat;
|
||||
/* set the nand_oobinfo to support FPGA H/W error detection */
|
||||
this->ecc.layout = &rtc_from4_nand_oobinfo;
|
||||
this->ecc.hwctl = rtc_from4_enable_hwecc;
|
||||
this->ecc.calculate = rtc_from4_calculate_ecc;
|
||||
this->ecc.correct = rtc_from4_correct_data;
|
||||
|
||||
/* We could create the decoder on demand, if memory is a concern.
|
||||
* This way we have it handy, if an error happens
|
||||
*
|
||||
* Symbolsize is 10 (bits)
|
||||
* Primitve polynomial is x^10+x^3+1
|
||||
* first consecutive root is 0
|
||||
* primitve element to generate roots = 1
|
||||
* generator polinomial degree = 6
|
||||
*/
|
||||
rs_decoder = init_rs(10, 0x409, 0, 1, 6);
|
||||
if (!rs_decoder) {
|
||||
printk(KERN_ERR "Could not create a RS decoder\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_1;
|
||||
}
|
||||
#else
|
||||
printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
|
||||
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
|
||||
/* set the bad block tables to support debugging */
|
||||
this->bbt_td = &rtc_from4_bbt_main_descr;
|
||||
this->bbt_md = &rtc_from4_bbt_mirror_descr;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) {
|
||||
ret = -ENXIO;
|
||||
goto err_2;
|
||||
}
|
||||
|
||||
/* Perform 'device recovery' for each chip in case there was a power loss. */
|
||||
for (i = 0; i < this->numchips; i++) {
|
||||
deplete(rtc_from4_mtd, i);
|
||||
}
|
||||
|
||||
#if RTC_FROM4_NO_VIRTBLOCKS
|
||||
/* use a smaller erase block to minimize wasted space when a block is bad */
|
||||
/* note: this uses eight times as much RAM as using the default and makes */
|
||||
/* mounts take four times as long. */
|
||||
rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS;
|
||||
#endif
|
||||
|
||||
/* Register the partitions */
|
||||
ret = mtd_device_register(rtc_from4_mtd, partition_info,
|
||||
NUM_PARTITIONS);
|
||||
if (ret)
|
||||
goto err_3;
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
err_3:
|
||||
nand_release(rtc_from4_mtd);
|
||||
err_2:
|
||||
free_rs(rs_decoder);
|
||||
err_1:
|
||||
kfree(rtc_from4_mtd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
module_init(rtc_from4_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit rtc_from4_cleanup(void)
|
||||
{
|
||||
/* Release resource, unregister partitions */
|
||||
nand_release(rtc_from4_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree(rtc_from4_mtd);
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
/* Free the reed solomon resources */
|
||||
if (rs_decoder) {
|
||||
free_rs(rs_decoder);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
module_exit(rtc_from4_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
|
||||
MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
|
|
@ -1081,7 +1081,6 @@ static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
|
|||
return pdata;
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
#define of_flctl_match NULL
|
||||
static struct sh_flctl_platform_data *flctl_parse_dt(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
|
@ -1219,22 +1218,11 @@ static struct platform_driver flctl_driver = {
|
|||
.driver = {
|
||||
.name = "sh_flctl",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_flctl_match,
|
||||
.of_match_table = of_match_ptr(of_flctl_match),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init flctl_nand_init(void)
|
||||
{
|
||||
return platform_driver_probe(&flctl_driver, flctl_probe);
|
||||
}
|
||||
|
||||
static void __exit flctl_nand_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&flctl_driver);
|
||||
}
|
||||
|
||||
module_init(flctl_nand_init);
|
||||
module_exit(flctl_nand_cleanup);
|
||||
module_platform_driver_probe(flctl_driver, flctl_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Yoshihiro Shimoda");
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sizes.h>
|
||||
#include "sm_common.h"
|
||||
|
||||
static struct nand_ecclayout nand_oob_sm = {
|
||||
|
@ -67,44 +68,37 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
return error;
|
||||
}
|
||||
|
||||
|
||||
static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
|
||||
{"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0},
|
||||
{"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0},
|
||||
{"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0},
|
||||
{"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0},
|
||||
{"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0},
|
||||
{"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND_ROM},
|
||||
{"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0},
|
||||
{"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0},
|
||||
{"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0},
|
||||
{"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM},
|
||||
{"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0},
|
||||
{"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM},
|
||||
{"SmartMedia 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0},
|
||||
{"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM},
|
||||
{"SmartMedia 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0},
|
||||
{"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM},
|
||||
{"SmartMedia 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0},
|
||||
{"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM},
|
||||
{"SmartMedia 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0},
|
||||
{"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM},
|
||||
{"SmartMedia 256MiB 3,3V", 0x71, 512, 256, 0x4000 },
|
||||
{"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM},
|
||||
{NULL,}
|
||||
LEGACY_ID_NAND("SmartMedia 2MiB 3,3V ROM", 0x5d, 2, SZ_8K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 4MiB 3,3V", 0xe3, 4, SZ_8K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 4MiB 3,3/5V", 0xe5, 4, SZ_8K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 4MiB 5V", 0x6b, 4, SZ_8K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 4MiB 3,3V ROM", 0xd5, 4, SZ_8K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 8MiB 3,3V", 0xe6, 8, SZ_8K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 8MiB 3,3V ROM", 0xd6, 8, SZ_8K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 16MiB 3,3V", 0x73, 16, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 16MiB 3,3V ROM", 0x57, 16, SZ_16K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 32MiB 3,3V", 0x75, 32, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 32MiB 3,3V ROM", 0x58, 32, SZ_16K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 64MiB 3,3V", 0x76, 64, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 64MiB 3,3V ROM", 0xd9, 64, SZ_16K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 128MiB 3,3V", 0x79, 128, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 128MiB 3,3V ROM", 0xda, 128, SZ_16K, NAND_ROM),
|
||||
LEGACY_ID_NAND("SmartMedia 256MiB 3, 3V", 0x71, 256, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("SmartMedia 256MiB 3,3V ROM", 0x5b, 256, SZ_16K, NAND_ROM),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static struct nand_flash_dev nand_xd_flash_ids[] = {
|
||||
|
||||
{"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0},
|
||||
{"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0},
|
||||
{"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0},
|
||||
{"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0},
|
||||
{"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, NAND_BROKEN_XD},
|
||||
{"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, NAND_BROKEN_XD},
|
||||
{"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, NAND_BROKEN_XD},
|
||||
{"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, NAND_BROKEN_XD},
|
||||
{NULL,}
|
||||
LEGACY_ID_NAND("xD 16MiB 3,3V", 0x73, 16, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("xD 32MiB 3,3V", 0x75, 32, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("xD 64MiB 3,3V", 0x76, 64, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("xD 128MiB 3,3V", 0x79, 128, SZ_16K, 0),
|
||||
LEGACY_ID_NAND("xD 256MiB 3,3V", 0x71, 256, SZ_16K, NAND_BROKEN_XD),
|
||||
LEGACY_ID_NAND("xD 512MiB 3,3V", 0xdc, 512, SZ_16K, NAND_BROKEN_XD),
|
||||
LEGACY_ID_NAND("xD 1GiB 3,3V", 0xd3, 1024, SZ_16K, NAND_BROKEN_XD),
|
||||
LEGACY_ID_NAND("xD 2GiB 3,3V", 0xd5, 2048, SZ_16K, NAND_BROKEN_XD),
|
||||
{NULL}
|
||||
};
|
||||
|
||||
int sm_register_device(struct mtd_info *mtd, int smartmedia)
|
||||
|
|
|
@ -427,18 +427,7 @@ static struct platform_driver txx9ndfmc_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
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_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver");
|
||||
|
|
|
@ -55,6 +55,7 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
|||
while ((pp = of_get_next_child(node, pp))) {
|
||||
const __be32 *reg;
|
||||
int len;
|
||||
int a_cells, s_cells;
|
||||
|
||||
reg = of_get_property(pp, "reg", &len);
|
||||
if (!reg) {
|
||||
|
@ -62,8 +63,10 @@ static int parse_ofpart_partitions(struct mtd_info *master,
|
|||
continue;
|
||||
}
|
||||
|
||||
(*pparts)[i].offset = be32_to_cpu(reg[0]);
|
||||
(*pparts)[i].size = be32_to_cpu(reg[1]);
|
||||
a_cells = of_n_addr_cells(pp);
|
||||
s_cells = of_n_size_cells(pp);
|
||||
(*pparts)[i].offset = of_read_number(reg, a_cells);
|
||||
(*pparts)[i].size = of_read_number(reg + a_cells, s_cells);
|
||||
|
||||
partname = of_get_property(pp, "label", &len);
|
||||
if (!partname)
|
||||
|
|
|
@ -40,7 +40,6 @@ config MTD_ONENAND_SAMSUNG
|
|||
|
||||
config MTD_ONENAND_OTP
|
||||
bool "OneNAND OTP Support"
|
||||
select HAVE_MTD_OTP
|
||||
help
|
||||
One Block of the NAND Flash Array memory is reserved as
|
||||
a One-Time Programmable Block memory area.
|
||||
|
@ -68,10 +67,4 @@ config MTD_ONENAND_2X_PROGRAM
|
|||
|
||||
And more recent chips
|
||||
|
||||
config MTD_ONENAND_SIM
|
||||
tristate "OneNAND simulator support"
|
||||
help
|
||||
The simulator may simulate various OneNAND flash chips for the
|
||||
OneNAND MTD layer.
|
||||
|
||||
endif # MTD_ONENAND
|
||||
|
|
|
@ -10,7 +10,4 @@ obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o
|
|||
obj-$(CONFIG_MTD_ONENAND_OMAP2) += omap2.o
|
||||
obj-$(CONFIG_MTD_ONENAND_SAMSUNG) += samsung.o
|
||||
|
||||
# Simulator
|
||||
obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o
|
||||
|
||||
onenand-objs = onenand_base.o onenand_bbt.o
|
||||
|
|
|
@ -832,19 +832,7 @@ static struct platform_driver omap2_onenand_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
static int __init omap2_onenand_init(void)
|
||||
{
|
||||
printk(KERN_INFO "OneNAND driver initializing\n");
|
||||
return platform_driver_register(&omap2_onenand_driver);
|
||||
}
|
||||
|
||||
static void __exit omap2_onenand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap2_onenand_driver);
|
||||
}
|
||||
|
||||
module_init(omap2_onenand_init);
|
||||
module_exit(omap2_onenand_exit);
|
||||
module_platform_driver(omap2_onenand_driver);
|
||||
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,564 +0,0 @@
|
|||
/*
|
||||
* linux/drivers/mtd/onenand/onenand_sim.c
|
||||
*
|
||||
* The OneNAND simulator
|
||||
*
|
||||
* Copyright © 2005-2007 Samsung Electronics
|
||||
* Kyungmin Park <kyungmin.park@samsung.com>
|
||||
*
|
||||
* Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
|
||||
* Flex-OneNAND simulator support
|
||||
* Copyright (C) Samsung Electronics, 2008
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/onenand.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
|
||||
#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
|
||||
#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04
|
||||
#endif
|
||||
|
||||
#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1)
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_VERSION_ID
|
||||
#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID
|
||||
#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND
|
||||
#endif
|
||||
|
||||
/* Initial boundary values for Flex-OneNAND Simulator */
|
||||
#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY
|
||||
#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY
|
||||
#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01
|
||||
#endif
|
||||
|
||||
static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER;
|
||||
static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID;
|
||||
static int version_id = CONFIG_ONENAND_SIM_VERSION_ID;
|
||||
static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID;
|
||||
static int boundary[] = {
|
||||
CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY,
|
||||
CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY,
|
||||
};
|
||||
|
||||
struct onenand_flash {
|
||||
void __iomem *base;
|
||||
void __iomem *data;
|
||||
};
|
||||
|
||||
#define ONENAND_CORE(flash) (flash->data)
|
||||
#define ONENAND_CORE_SPARE(flash, this, offset) \
|
||||
((flash->data) + (this->chipsize) + (offset >> 5))
|
||||
|
||||
#define ONENAND_MAIN_AREA(this, offset) \
|
||||
(this->base + ONENAND_DATARAM + offset)
|
||||
|
||||
#define ONENAND_SPARE_AREA(this, offset) \
|
||||
(this->base + ONENAND_SPARERAM + offset)
|
||||
|
||||
#define ONENAND_GET_WP_STATUS(this) \
|
||||
(readw(this->base + ONENAND_REG_WP_STATUS))
|
||||
|
||||
#define ONENAND_SET_WP_STATUS(v, this) \
|
||||
(writew(v, this->base + ONENAND_REG_WP_STATUS))
|
||||
|
||||
/* It has all 0xff chars */
|
||||
#define MAX_ONENAND_PAGESIZE (4096 + 128)
|
||||
static unsigned char *ffchars;
|
||||
|
||||
#if CONFIG_FLEXONENAND
|
||||
#define PARTITION_NAME "Flex-OneNAND simulator partition"
|
||||
#else
|
||||
#define PARTITION_NAME "OneNAND simulator partition"
|
||||
#endif
|
||||
|
||||
static struct mtd_partition os_partitions[] = {
|
||||
{
|
||||
.name = PARTITION_NAME,
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* OneNAND simulator mtd
|
||||
*/
|
||||
struct onenand_info {
|
||||
struct mtd_info mtd;
|
||||
struct mtd_partition *parts;
|
||||
struct onenand_chip onenand;
|
||||
struct onenand_flash flash;
|
||||
};
|
||||
|
||||
static struct onenand_info *info;
|
||||
|
||||
#define DPRINTK(format, args...) \
|
||||
do { \
|
||||
printk(KERN_DEBUG "%s[%d]: " format "\n", __func__, \
|
||||
__LINE__, ##args); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* onenand_lock_handle - Handle Lock scheme
|
||||
* @this: OneNAND device structure
|
||||
* @cmd: The command to be sent
|
||||
*
|
||||
* Send lock command to OneNAND device.
|
||||
* The lock scheme depends on chip type.
|
||||
*/
|
||||
static void onenand_lock_handle(struct onenand_chip *this, int cmd)
|
||||
{
|
||||
int block_lock_scheme;
|
||||
int status;
|
||||
|
||||
status = ONENAND_GET_WP_STATUS(this);
|
||||
block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK);
|
||||
|
||||
switch (cmd) {
|
||||
case ONENAND_CMD_UNLOCK:
|
||||
case ONENAND_CMD_UNLOCK_ALL:
|
||||
if (block_lock_scheme)
|
||||
ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
|
||||
else
|
||||
ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this);
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_LOCK:
|
||||
if (block_lock_scheme)
|
||||
ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this);
|
||||
else
|
||||
ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this);
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_LOCK_TIGHT:
|
||||
if (block_lock_scheme)
|
||||
ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this);
|
||||
else
|
||||
ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_bootram_handle - Handle BootRAM area
|
||||
* @this: OneNAND device structure
|
||||
* @cmd: The command to be sent
|
||||
*
|
||||
* Emulate BootRAM area. It is possible to do basic operation using BootRAM.
|
||||
*/
|
||||
static void onenand_bootram_handle(struct onenand_chip *this, int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case ONENAND_CMD_READID:
|
||||
writew(manuf_id, this->base);
|
||||
writew(device_id, this->base + 2);
|
||||
writew(version_id, this->base + 4);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* REVIST: Handle other commands */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_update_interrupt - Set interrupt register
|
||||
* @this: OneNAND device structure
|
||||
* @cmd: The command to be sent
|
||||
*
|
||||
* Update interrupt register. The status depends on command.
|
||||
*/
|
||||
static void onenand_update_interrupt(struct onenand_chip *this, int cmd)
|
||||
{
|
||||
int interrupt = ONENAND_INT_MASTER;
|
||||
|
||||
switch (cmd) {
|
||||
case ONENAND_CMD_READ:
|
||||
case ONENAND_CMD_READOOB:
|
||||
interrupt |= ONENAND_INT_READ;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_PROG:
|
||||
case ONENAND_CMD_PROGOOB:
|
||||
interrupt |= ONENAND_INT_WRITE;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_ERASE:
|
||||
interrupt |= ONENAND_INT_ERASE;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_RESET:
|
||||
interrupt |= ONENAND_INT_RESET;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writew(interrupt, this->base + ONENAND_REG_INTERRUPT);
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_check_overwrite - Check if over-write happened
|
||||
* @dest: The destination pointer
|
||||
* @src: The source pointer
|
||||
* @count: The length to be check
|
||||
*
|
||||
* Returns: 0 on same, otherwise 1
|
||||
*
|
||||
* Compare the source with destination
|
||||
*/
|
||||
static int onenand_check_overwrite(void *dest, void *src, size_t count)
|
||||
{
|
||||
unsigned int *s = (unsigned int *) src;
|
||||
unsigned int *d = (unsigned int *) dest;
|
||||
int i;
|
||||
|
||||
count >>= 2;
|
||||
for (i = 0; i < count; i++)
|
||||
if ((*s++ ^ *d++) != 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_data_handle - Handle OneNAND Core and DataRAM
|
||||
* @this: OneNAND device structure
|
||||
* @cmd: The command to be sent
|
||||
* @dataram: Which dataram used
|
||||
* @offset: The offset to OneNAND Core
|
||||
*
|
||||
* Copy data from OneNAND Core to DataRAM (read)
|
||||
* Copy data from DataRAM to OneNAND Core (write)
|
||||
* Erase the OneNAND Core (erase)
|
||||
*/
|
||||
static void onenand_data_handle(struct onenand_chip *this, int cmd,
|
||||
int dataram, unsigned int offset)
|
||||
{
|
||||
struct mtd_info *mtd = &info->mtd;
|
||||
struct onenand_flash *flash = this->priv;
|
||||
int main_offset, spare_offset, die = 0;
|
||||
void __iomem *src;
|
||||
void __iomem *dest;
|
||||
unsigned int i;
|
||||
static int pi_operation;
|
||||
int erasesize, rgn;
|
||||
|
||||
if (dataram) {
|
||||
main_offset = mtd->writesize;
|
||||
spare_offset = mtd->oobsize;
|
||||
} else {
|
||||
main_offset = 0;
|
||||
spare_offset = 0;
|
||||
}
|
||||
|
||||
if (pi_operation) {
|
||||
die = readw(this->base + ONENAND_REG_START_ADDRESS2);
|
||||
die >>= ONENAND_DDP_SHIFT;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case FLEXONENAND_CMD_PI_ACCESS:
|
||||
pi_operation = 1;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_RESET:
|
||||
pi_operation = 0;
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_READ:
|
||||
src = ONENAND_CORE(flash) + offset;
|
||||
dest = ONENAND_MAIN_AREA(this, main_offset);
|
||||
if (pi_operation) {
|
||||
writew(boundary[die], this->base + ONENAND_DATARAM);
|
||||
break;
|
||||
}
|
||||
memcpy(dest, src, mtd->writesize);
|
||||
/* Fall through */
|
||||
|
||||
case ONENAND_CMD_READOOB:
|
||||
src = ONENAND_CORE_SPARE(flash, this, offset);
|
||||
dest = ONENAND_SPARE_AREA(this, spare_offset);
|
||||
memcpy(dest, src, mtd->oobsize);
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_PROG:
|
||||
src = ONENAND_MAIN_AREA(this, main_offset);
|
||||
dest = ONENAND_CORE(flash) + offset;
|
||||
if (pi_operation) {
|
||||
boundary[die] = readw(this->base + ONENAND_DATARAM);
|
||||
break;
|
||||
}
|
||||
/* To handle partial write */
|
||||
for (i = 0; i < (1 << mtd->subpage_sft); i++) {
|
||||
int off = i * this->subpagesize;
|
||||
if (!memcmp(src + off, ffchars, this->subpagesize))
|
||||
continue;
|
||||
if (memcmp(dest + off, ffchars, this->subpagesize) &&
|
||||
onenand_check_overwrite(dest + off, src + off, this->subpagesize))
|
||||
printk(KERN_ERR "over-write happened at 0x%08x\n", offset);
|
||||
memcpy(dest + off, src + off, this->subpagesize);
|
||||
}
|
||||
/* Fall through */
|
||||
|
||||
case ONENAND_CMD_PROGOOB:
|
||||
src = ONENAND_SPARE_AREA(this, spare_offset);
|
||||
/* Check all data is 0xff chars */
|
||||
if (!memcmp(src, ffchars, mtd->oobsize))
|
||||
break;
|
||||
|
||||
dest = ONENAND_CORE_SPARE(flash, this, offset);
|
||||
if (memcmp(dest, ffchars, mtd->oobsize) &&
|
||||
onenand_check_overwrite(dest, src, mtd->oobsize))
|
||||
printk(KERN_ERR "OOB: over-write happened at 0x%08x\n",
|
||||
offset);
|
||||
memcpy(dest, src, mtd->oobsize);
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_ERASE:
|
||||
if (pi_operation)
|
||||
break;
|
||||
|
||||
if (FLEXONENAND(this)) {
|
||||
rgn = flexonenand_region(mtd, offset);
|
||||
erasesize = mtd->eraseregions[rgn].erasesize;
|
||||
} else
|
||||
erasesize = mtd->erasesize;
|
||||
|
||||
memset(ONENAND_CORE(flash) + offset, 0xff, erasesize);
|
||||
memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
|
||||
(erasesize >> 5));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_command_handle - Handle command
|
||||
* @this: OneNAND device structure
|
||||
* @cmd: The command to be sent
|
||||
*
|
||||
* Emulate OneNAND command.
|
||||
*/
|
||||
static void onenand_command_handle(struct onenand_chip *this, int cmd)
|
||||
{
|
||||
unsigned long offset = 0;
|
||||
int block = -1, page = -1, bufferram = -1;
|
||||
int dataram = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case ONENAND_CMD_UNLOCK:
|
||||
case ONENAND_CMD_LOCK:
|
||||
case ONENAND_CMD_LOCK_TIGHT:
|
||||
case ONENAND_CMD_UNLOCK_ALL:
|
||||
onenand_lock_handle(this, cmd);
|
||||
break;
|
||||
|
||||
case ONENAND_CMD_BUFFERRAM:
|
||||
/* Do nothing */
|
||||
return;
|
||||
|
||||
default:
|
||||
block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1);
|
||||
if (block & (1 << ONENAND_DDP_SHIFT)) {
|
||||
block &= ~(1 << ONENAND_DDP_SHIFT);
|
||||
/* The half of chip block */
|
||||
block += this->chipsize >> (this->erase_shift + 1);
|
||||
}
|
||||
if (cmd == ONENAND_CMD_ERASE)
|
||||
break;
|
||||
|
||||
page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8);
|
||||
page = (page >> ONENAND_FPA_SHIFT);
|
||||
bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER);
|
||||
bufferram >>= ONENAND_BSA_SHIFT;
|
||||
bufferram &= ONENAND_BSA_DATARAM1;
|
||||
dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (block != -1)
|
||||
offset = onenand_addr(this, block);
|
||||
|
||||
if (page != -1)
|
||||
offset += page << this->page_shift;
|
||||
|
||||
onenand_data_handle(this, cmd, dataram, offset);
|
||||
|
||||
onenand_update_interrupt(this, cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* onenand_writew - [OneNAND Interface] Emulate write operation
|
||||
* @value: value to write
|
||||
* @addr: address to write
|
||||
*
|
||||
* Write OneNAND register with value
|
||||
*/
|
||||
static void onenand_writew(unsigned short value, void __iomem * addr)
|
||||
{
|
||||
struct onenand_chip *this = info->mtd.priv;
|
||||
|
||||
/* BootRAM handling */
|
||||
if (addr < this->base + ONENAND_DATARAM) {
|
||||
onenand_bootram_handle(this, value);
|
||||
return;
|
||||
}
|
||||
/* Command handling */
|
||||
if (addr == this->base + ONENAND_REG_COMMAND)
|
||||
onenand_command_handle(this, value);
|
||||
|
||||
writew(value, addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* flash_init - Initialize OneNAND simulator
|
||||
* @flash: OneNAND simulator data strucutres
|
||||
*
|
||||
* Initialize OneNAND simulator.
|
||||
*/
|
||||
static int __init flash_init(struct onenand_flash *flash)
|
||||
{
|
||||
int density, size;
|
||||
int buffer_size;
|
||||
|
||||
flash->base = kzalloc(131072, GFP_KERNEL);
|
||||
if (!flash->base) {
|
||||
printk(KERN_ERR "Unable to allocate base address.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
|
||||
density &= ONENAND_DEVICE_DENSITY_MASK;
|
||||
size = ((16 << 20) << density);
|
||||
|
||||
ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
|
||||
if (!ONENAND_CORE(flash)) {
|
||||
printk(KERN_ERR "Unable to allocate nand core address.\n");
|
||||
kfree(flash->base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memset(ONENAND_CORE(flash), 0xff, size + (size >> 5));
|
||||
|
||||
/* Setup registers */
|
||||
writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
|
||||
writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
|
||||
writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
|
||||
writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY);
|
||||
|
||||
if (density < 2 && (!CONFIG_FLEXONENAND))
|
||||
buffer_size = 0x0400; /* 1KiB page */
|
||||
else
|
||||
buffer_size = 0x0800; /* 2KiB page */
|
||||
writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* flash_exit - Clean up OneNAND simulator
|
||||
* @flash: OneNAND simulator data structures
|
||||
*
|
||||
* Clean up OneNAND simulator.
|
||||
*/
|
||||
static void flash_exit(struct onenand_flash *flash)
|
||||
{
|
||||
vfree(ONENAND_CORE(flash));
|
||||
kfree(flash->base);
|
||||
}
|
||||
|
||||
static int __init onenand_sim_init(void)
|
||||
{
|
||||
/* Allocate all 0xff chars pointer */
|
||||
ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL);
|
||||
if (!ffchars) {
|
||||
printk(KERN_ERR "Unable to allocate ff chars.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE);
|
||||
|
||||
/* Allocate OneNAND simulator mtd pointer */
|
||||
info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
printk(KERN_ERR "Unable to allocate core structures.\n");
|
||||
kfree(ffchars);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Override write_word function */
|
||||
info->onenand.write_word = onenand_writew;
|
||||
|
||||
if (flash_init(&info->flash)) {
|
||||
printk(KERN_ERR "Unable to allocate flash.\n");
|
||||
kfree(ffchars);
|
||||
kfree(info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->parts = os_partitions;
|
||||
|
||||
info->onenand.base = info->flash.base;
|
||||
info->onenand.priv = &info->flash;
|
||||
|
||||
info->mtd.name = "OneNAND simulator";
|
||||
info->mtd.priv = &info->onenand;
|
||||
info->mtd.owner = THIS_MODULE;
|
||||
|
||||
if (onenand_scan(&info->mtd, 1)) {
|
||||
flash_exit(&info->flash);
|
||||
kfree(ffchars);
|
||||
kfree(info);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mtd_device_register(&info->mtd, info->parts,
|
||||
ARRAY_SIZE(os_partitions));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit onenand_sim_exit(void)
|
||||
{
|
||||
struct onenand_chip *this = info->mtd.priv;
|
||||
struct onenand_flash *flash = this->priv;
|
||||
|
||||
onenand_release(&info->mtd);
|
||||
flash_exit(flash);
|
||||
kfree(ffchars);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
module_init(onenand_sim_init);
|
||||
module_exit(onenand_sim_exit);
|
||||
|
||||
MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
|
||||
MODULE_DESCRIPTION("The OneNAND flash simulator");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include "ssb_private.h"
|
||||
|
||||
static const char *part_probes[] = { "bcm47xxpart", NULL };
|
||||
static const char * const part_probes[] = { "bcm47xxpart", NULL };
|
||||
|
||||
static struct physmap_flash_data ssb_pflash_data = {
|
||||
.part_probe_types = part_probes,
|
||||
|
|
|
@ -362,10 +362,10 @@ struct mtd_partition;
|
|||
struct mtd_part_parser_data;
|
||||
|
||||
extern int mtd_device_parse_register(struct mtd_info *mtd,
|
||||
const char **part_probe_types,
|
||||
struct mtd_part_parser_data *parser_data,
|
||||
const struct mtd_partition *defparts,
|
||||
int defnr_parts);
|
||||
const char * const *part_probe_types,
|
||||
struct mtd_part_parser_data *parser_data,
|
||||
const struct mtd_partition *defparts,
|
||||
int defnr_parts);
|
||||
#define mtd_device_register(master, parts, nr_parts) \
|
||||
mtd_device_parse_register(master, NULL, NULL, parts, nr_parts)
|
||||
extern int mtd_device_unregister(struct mtd_info *master);
|
||||
|
|
|
@ -86,7 +86,6 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
|||
#define NAND_CMD_READOOB 0x50
|
||||
#define NAND_CMD_ERASE1 0x60
|
||||
#define NAND_CMD_STATUS 0x70
|
||||
#define NAND_CMD_STATUS_MULTI 0x71
|
||||
#define NAND_CMD_SEQIN 0x80
|
||||
#define NAND_CMD_RNDIN 0x85
|
||||
#define NAND_CMD_READID 0x90
|
||||
|
@ -105,25 +104,6 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
|
|||
#define NAND_CMD_RNDOUTSTART 0xE0
|
||||
#define NAND_CMD_CACHEDPROG 0x15
|
||||
|
||||
/* Extended commands for AG-AND device */
|
||||
/*
|
||||
* Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
|
||||
* there is no way to distinguish that from NAND_CMD_READ0
|
||||
* until the remaining sequence of commands has been completed
|
||||
* so add a high order bit and mask it off in the command.
|
||||
*/
|
||||
#define NAND_CMD_DEPLETE1 0x100
|
||||
#define NAND_CMD_DEPLETE2 0x38
|
||||
#define NAND_CMD_STATUS_MULTI 0x71
|
||||
#define NAND_CMD_STATUS_ERROR 0x72
|
||||
/* multi-bank error status (banks 0-3) */
|
||||
#define NAND_CMD_STATUS_ERROR0 0x73
|
||||
#define NAND_CMD_STATUS_ERROR1 0x74
|
||||
#define NAND_CMD_STATUS_ERROR2 0x75
|
||||
#define NAND_CMD_STATUS_ERROR3 0x76
|
||||
#define NAND_CMD_STATUS_RESET 0x7f
|
||||
#define NAND_CMD_STATUS_CLEAR 0xff
|
||||
|
||||
#define NAND_CMD_NONE -1
|
||||
|
||||
/* Status bits */
|
||||
|
@ -165,28 +145,8 @@ typedef enum {
|
|||
*/
|
||||
/* Buswidth is 16 bit */
|
||||
#define NAND_BUSWIDTH_16 0x00000002
|
||||
/* Device supports partial programming without padding */
|
||||
#define NAND_NO_PADDING 0x00000004
|
||||
/* Chip has cache program function */
|
||||
#define NAND_CACHEPRG 0x00000008
|
||||
/* Chip has copy back function */
|
||||
#define NAND_COPYBACK 0x00000010
|
||||
/*
|
||||
* AND Chip which has 4 banks and a confusing page / block
|
||||
* assignment. See Renesas datasheet for further information.
|
||||
*/
|
||||
#define NAND_IS_AND 0x00000020
|
||||
/*
|
||||
* Chip has a array of 4 pages which can be read without
|
||||
* additional ready /busy waits.
|
||||
*/
|
||||
#define NAND_4PAGE_ARRAY 0x00000040
|
||||
/*
|
||||
* Chip requires that BBT is periodically rewritten to prevent
|
||||
* bits from adjacent blocks from 'leaking' in altering data.
|
||||
* This happens with the Renesas AG-AND chips, possibly others.
|
||||
*/
|
||||
#define BBT_AUTO_REFRESH 0x00000080
|
||||
/*
|
||||
* Chip requires ready check on read (for auto-incremented sequential read).
|
||||
* True only for small page devices; large page devices do not support
|
||||
|
@ -207,13 +167,10 @@ typedef enum {
|
|||
#define NAND_SUBPAGE_READ 0x00001000
|
||||
|
||||
/* Options valid for Samsung large page devices */
|
||||
#define NAND_SAMSUNG_LP_OPTIONS \
|
||||
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
|
||||
#define NAND_SAMSUNG_LP_OPTIONS NAND_CACHEPRG
|
||||
|
||||
/* Macros to identify the above */
|
||||
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
|
||||
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
|
||||
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
|
||||
#define NAND_HAS_SUBPAGE_READ(chip) ((chip->options & NAND_SUBPAGE_READ))
|
||||
|
||||
/* Non chip related options */
|
||||
|
@ -361,6 +318,7 @@ struct nand_hw_control {
|
|||
* any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
|
||||
* @read_subpage: function to read parts of the page covered by ECC;
|
||||
* returns same as read_page()
|
||||
* @write_subpage: function to write parts of the page covered by ECC.
|
||||
* @write_page: function to write a page according to the ECC generator
|
||||
* requirements.
|
||||
* @write_oob_raw: function to write chip OOB data without ECC
|
||||
|
@ -392,6 +350,9 @@ struct nand_ecc_ctrl {
|
|||
uint8_t *buf, int oob_required, int page);
|
||||
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offs, uint32_t len, uint8_t *buf);
|
||||
int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
uint32_t offset, uint32_t data_len,
|
||||
const uint8_t *data_buf, int oob_required);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required);
|
||||
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
|
@ -527,8 +488,8 @@ struct nand_chip {
|
|||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
|
||||
int status, int page);
|
||||
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
const uint8_t *buf, int oob_required, int page,
|
||||
int cached, int raw);
|
||||
uint32_t offset, int data_len, const uint8_t *buf,
|
||||
int oob_required, int page, int cached, int raw);
|
||||
int (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
int feature_addr, uint8_t *subfeature_para);
|
||||
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
|
@ -589,25 +550,65 @@ struct nand_chip {
|
|||
#define NAND_MFR_MACRONIX 0xc2
|
||||
#define NAND_MFR_EON 0x92
|
||||
|
||||
/* The maximum expected count of bytes in the NAND ID sequence */
|
||||
#define NAND_MAX_ID_LEN 8
|
||||
|
||||
/*
|
||||
* A helper for defining older NAND chips where the second ID byte fully
|
||||
* defined the chip, including the geometry (chip size, eraseblock size, page
|
||||
* size). All these chips have 512 bytes NAND page size.
|
||||
*/
|
||||
#define LEGACY_ID_NAND(nm, devid, chipsz, erasesz, opts) \
|
||||
{ .name = (nm), {{ .dev_id = (devid) }}, .pagesize = 512, \
|
||||
.chipsize = (chipsz), .erasesize = (erasesz), .options = (opts) }
|
||||
|
||||
/*
|
||||
* A helper for defining newer chips which report their page size and
|
||||
* eraseblock size via the extended ID bytes.
|
||||
*
|
||||
* The real difference between LEGACY_ID_NAND and EXTENDED_ID_NAND is that with
|
||||
* EXTENDED_ID_NAND, manufacturers overloaded the same device ID so that the
|
||||
* device ID now only represented a particular total chip size (and voltage,
|
||||
* buswidth), and the page size, eraseblock size, and OOB size could vary while
|
||||
* using the same device ID.
|
||||
*/
|
||||
#define EXTENDED_ID_NAND(nm, devid, chipsz, opts) \
|
||||
{ .name = (nm), {{ .dev_id = (devid) }}, .chipsize = (chipsz), \
|
||||
.options = (opts) }
|
||||
|
||||
/**
|
||||
* struct nand_flash_dev - NAND Flash Device ID Structure
|
||||
* @name: Identify the device type
|
||||
* @id: device ID code
|
||||
* @pagesize: Pagesize in bytes. Either 256 or 512 or 0
|
||||
* If the pagesize is 0, then the real pagesize
|
||||
* and the eraseize are determined from the
|
||||
* extended id bytes in the chip
|
||||
* @erasesize: Size of an erase block in the flash device.
|
||||
* @chipsize: Total chipsize in Mega Bytes
|
||||
* @options: Bitfield to store chip relevant options
|
||||
* @name: a human-readable name of the NAND chip
|
||||
* @dev_id: the device ID (the second byte of the full chip ID array)
|
||||
* @mfr_id: manufecturer ID part of the full chip ID array (refers the same
|
||||
* memory address as @id[0])
|
||||
* @dev_id: device ID part of the full chip ID array (refers the same memory
|
||||
* address as @id[1])
|
||||
* @id: full device ID array
|
||||
* @pagesize: size of the NAND page in bytes; if 0, then the real page size (as
|
||||
* well as the eraseblock size) is determined from the extended NAND
|
||||
* chip ID array)
|
||||
* @chipsize: total chip size in MiB
|
||||
* @erasesize: eraseblock size in bytes (determined from the extended ID if 0)
|
||||
* @options: stores various chip bit options
|
||||
* @id_len: The valid length of the @id.
|
||||
* @oobsize: OOB size
|
||||
*/
|
||||
struct nand_flash_dev {
|
||||
char *name;
|
||||
int id;
|
||||
unsigned long pagesize;
|
||||
unsigned long chipsize;
|
||||
unsigned long erasesize;
|
||||
unsigned long options;
|
||||
union {
|
||||
struct {
|
||||
uint8_t mfr_id;
|
||||
uint8_t dev_id;
|
||||
};
|
||||
uint8_t id[NAND_MAX_ID_LEN];
|
||||
};
|
||||
unsigned int pagesize;
|
||||
unsigned int chipsize;
|
||||
unsigned int erasesize;
|
||||
unsigned int options;
|
||||
uint16_t id_len;
|
||||
uint16_t oobsize;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -30,7 +30,7 @@ struct physmap_flash_data {
|
|||
unsigned int pfow_base;
|
||||
char *probe_type;
|
||||
struct mtd_partition *parts;
|
||||
const char **part_probe_types;
|
||||
const char * const *part_probe_types;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MTD_PHYSMAP__ */
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
struct platdata_mtd_ram {
|
||||
const char *mapname;
|
||||
const char **map_probes;
|
||||
const char **probes;
|
||||
const char * const *map_probes;
|
||||
const char * const *probes;
|
||||
struct mtd_partition *partitions;
|
||||
int nr_partitions;
|
||||
int bankwidth;
|
||||
|
|
|
@ -50,5 +50,5 @@ struct elm_errorvec {
|
|||
|
||||
void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
|
||||
struct elm_errorvec *err_vec);
|
||||
void elm_config(struct device *dev, enum bch_ecc bch_type);
|
||||
int elm_config(struct device *dev, enum bch_ecc bch_type);
|
||||
#endif /* __ELM_H */
|
||||
|
|
Loading…
Reference in New Issue