diff options
| -rw-r--r-- | drivers/mtd/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/mtd/Makefile | 4 | ||||
| -rw-r--r-- | drivers/mtd/onenand/Kconfig | 32 | ||||
| -rw-r--r-- | drivers/mtd/onenand/Makefile | 9 | ||||
| -rw-r--r-- | drivers/mtd/onenand/omap-onenand.c | 178 | ||||
| -rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 1462 | ||||
| -rw-r--r-- | include/linux/mtd/onenand.h | 134 | ||||
| -rw-r--r-- | include/linux/mtd/onenand_regs.h | 167 |
8 files changed, 1990 insertions, 6 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 843a1cbe0866..3dbfbafb3481 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | # $Id: Kconfig,v 1.9 2005/06/16 08:49:29 sean Exp $ | 1 | # $Id: Kconfig,v 1.10 2005/07/11 10:39:27 gleixner Exp $ |
| 2 | 2 | ||
| 3 | menu "Memory Technology Devices (MTD)" | 3 | menu "Memory Technology Devices (MTD)" |
| 4 | 4 | ||
| @@ -259,9 +259,9 @@ config RFD_FTL | |||
| 259 | ---help--- | 259 | ---help--- |
| 260 | This provides support for the flash translation layer known | 260 | This provides support for the flash translation layer known |
| 261 | as the Resident Flash Disk (RFD), as used by the Embedded BIOS | 261 | as the Resident Flash Disk (RFD), as used by the Embedded BIOS |
| 262 | of General Software. | 262 | of General Software. There is a blurb at: |
| 263 | See http://www.gensw.com/pages/prod/bios/rfd.htm for further | 263 | |
| 264 | information. | 264 | http://www.gensw.com/pages/prod/bios/rfd.htm |
| 265 | 265 | ||
| 266 | source "drivers/mtd/chips/Kconfig" | 266 | source "drivers/mtd/chips/Kconfig" |
| 267 | 267 | ||
| @@ -271,5 +271,7 @@ source "drivers/mtd/devices/Kconfig" | |||
| 271 | 271 | ||
| 272 | source "drivers/mtd/nand/Kconfig" | 272 | source "drivers/mtd/nand/Kconfig" |
| 273 | 273 | ||
| 274 | source "drivers/mtd/onenand/Kconfig" | ||
| 275 | |||
| 274 | endmenu | 276 | endmenu |
| 275 | 277 | ||
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index cb16b7d478ce..fc9374407c2b 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | # | 1 | # |
| 2 | # Makefile for the memory technology device drivers. | 2 | # Makefile for the memory technology device drivers. |
| 3 | # | 3 | # |
| 4 | # $Id: Makefile.common,v 1.6 2005/06/16 08:49:29 sean Exp $ | 4 | # $Id: Makefile.common,v 1.7 2005/07/11 10:39:27 gleixner Exp $ |
| 5 | 5 | ||
| 6 | # Core functionality. | 6 | # Core functionality. |
| 7 | mtd-y := mtdcore.o | 7 | mtd-y := mtdcore.o |
| @@ -25,4 +25,4 @@ obj-$(CONFIG_RFD_FTL) += rfd_ftl.o mtd_blkdevs.o | |||
| 25 | nftl-objs := nftlcore.o nftlmount.o | 25 | nftl-objs := nftlcore.o nftlmount.o |
| 26 | inftl-objs := inftlcore.o inftlmount.o | 26 | inftl-objs := inftlcore.o inftlmount.o |
| 27 | 27 | ||
| 28 | obj-y += chips/ maps/ devices/ nand/ | 28 | obj-y += chips/ maps/ devices/ nand/ onenand/ |
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig new file mode 100644 index 000000000000..7d76ede984d8 --- /dev/null +++ b/drivers/mtd/onenand/Kconfig | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | # | ||
| 2 | # linux/drivers/mtd/onenand/Kconfig | ||
| 3 | # | ||
| 4 | |||
| 5 | menu "OneNAND Flash Device Drivers (EXPERIMENTAL)" | ||
| 6 | depends on MTD != n && EXPERIMENTAL | ||
| 7 | |||
| 8 | config MTD_ONENAND | ||
| 9 | tristate "OneNAND Device Support" | ||
| 10 | depends on MTD | ||
| 11 | help | ||
| 12 | This enables support for accessing all type of OneNAND flash | ||
| 13 | devices. For further information see | ||
| 14 | <http://www.samsung.com/Products/Semiconductor/Flash/OneNAND_TM/index.htm>. | ||
| 15 | |||
| 16 | config MTD_ONENAND_VERIFY_WRITE | ||
| 17 | bool "Verify OneNAND page writes" | ||
| 18 | depends on MTD_ONENAND | ||
| 19 | help | ||
| 20 | This adds an extra check when data is written to the flash. The | ||
| 21 | OneNAND flash device internally checks only bits transitioning | ||
| 22 | from 1 to 0. There is a rare possibility that even though the | ||
| 23 | device thinks the write was successful, a bit could have been | ||
| 24 | flipped accidentaly due to device wear or something else. | ||
| 25 | |||
| 26 | config MTD_ONENAND_OMAP | ||
| 27 | tristate "OneNAND Flash device on OMAP board" | ||
| 28 | depends on ARCH_OMAP && MTD_ONENAND | ||
| 29 | help | ||
| 30 | Support for OneNAND flash on TI OMAP board. | ||
| 31 | |||
| 32 | endmenu | ||
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile new file mode 100644 index 000000000000..f4e75864d8b5 --- /dev/null +++ b/drivers/mtd/onenand/Makefile | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # | ||
| 2 | # Makefile for the OneNAND MTD | ||
| 3 | # | ||
| 4 | |||
| 5 | # Core functionality. | ||
| 6 | obj-$(CONFIG_MTD_ONENAND) += onenand_base.o | ||
| 7 | |||
| 8 | # Board specific. | ||
| 9 | obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o | ||
diff --git a/drivers/mtd/onenand/omap-onenand.c b/drivers/mtd/onenand/omap-onenand.c new file mode 100644 index 000000000000..56e1aec6b835 --- /dev/null +++ b/drivers/mtd/onenand/omap-onenand.c | |||
| @@ -0,0 +1,178 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/mtd/onenand/omap-onenand.c | ||
| 3 | * | ||
| 4 | * Copyright (c) 2005 Samsung Electronics | ||
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
| 6 | * | ||
| 7 | * Derived from linux/drivers/mtd/nand/omap-nand-flash.c | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * Overview: | ||
| 14 | * This is a device driver for the OneNAND flash device for TI OMAP boards. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/slab.h> | ||
| 18 | #include <linux/init.h> | ||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/mtd/mtd.h> | ||
| 21 | #include <linux/mtd/onenand.h> | ||
| 22 | #include <linux/mtd/partitions.h> | ||
| 23 | |||
| 24 | #include <asm/io.h> | ||
| 25 | #include <asm/arch/hardware.h> | ||
| 26 | #include <asm/arch/tc.h> | ||
| 27 | #include <asm/sizes.h> | ||
| 28 | |||
| 29 | #define OMAP_ONENAND_FLASH_START1 OMAP_CS2A_PHYS | ||
| 30 | #define OMAP_ONENAND_FLASH_START2 OMAP_CS0_PHYS | ||
| 31 | /* | ||
| 32 | * MTD structure for OMAP board | ||
| 33 | */ | ||
| 34 | static struct mtd_info *omap_onenand_mtd = NULL; | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Define partitions for flash devices | ||
| 38 | */ | ||
| 39 | |||
| 40 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 41 | static struct mtd_partition static_partition[] = { | ||
| 42 | { | ||
| 43 | .name = "X-Loader + U-Boot", | ||
| 44 | .offset = 0, | ||
| 45 | .size = SZ_128K, | ||
| 46 | .mask_flags = MTD_WRITEABLE /* force read-only */ | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | .name = "U-Boot Environment", | ||
| 50 | .offset = MTDPART_OFS_APPEND, | ||
| 51 | .size = SZ_128K, | ||
| 52 | .mask_flags = MTD_WRITEABLE /* force read-only */ | ||
| 53 | }, | ||
| 54 | { | ||
| 55 | .name = "kernel", | ||
| 56 | .offset = MTDPART_OFS_APPEND, | ||
| 57 | .size = 2 * SZ_1M | ||
| 58 | }, | ||
| 59 | { | ||
| 60 | .name = "filesystem0", | ||
| 61 | .offset = MTDPART_OFS_APPEND, | ||
| 62 | .size = SZ_16M, | ||
| 63 | }, | ||
| 64 | { | ||
| 65 | .name = "filesystem1", | ||
| 66 | .offset = MTDPART_OFS_APPEND, | ||
| 67 | .size = MTDPART_SIZ_FULL, | ||
| 68 | }, | ||
| 69 | }; | ||
| 70 | |||
| 71 | const char *part_probes[] = { "cmdlinepart", NULL, }; | ||
| 72 | |||
| 73 | #endif | ||
| 74 | |||
| 75 | /* Scan to find existance of the device at base. | ||
| 76 | This also allocates oob and data internal buffers */ | ||
| 77 | static char onenand_name[] = "onenand"; | ||
| 78 | |||
| 79 | /* | ||
| 80 | * Main initialization routine | ||
| 81 | */ | ||
| 82 | static int __init omap_onenand_init (void) | ||
| 83 | { | ||
| 84 | struct onenand_chip *this; | ||
| 85 | struct mtd_partition *dynamic_partition = 0; | ||
| 86 | int err = 0; | ||
| 87 | |||
| 88 | /* Allocate memory for MTD device structure and private data */ | ||
| 89 | omap_onenand_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct onenand_chip), | ||
| 90 | GFP_KERNEL); | ||
| 91 | if (!omap_onenand_mtd) { | ||
| 92 | printk (KERN_WARNING "Unable to allocate OneNAND MTD device structure.\n"); | ||
| 93 | err = -ENOMEM; | ||
| 94 | goto out; | ||
| 95 | } | ||
| 96 | |||
| 97 | /* Get pointer to private data */ | ||
| 98 | this = (struct onenand_chip *) (&omap_onenand_mtd[1]); | ||
| 99 | |||
| 100 | /* Initialize structures */ | ||
| 101 | memset((char *) omap_onenand_mtd, 0, sizeof(struct mtd_info) + sizeof(struct onenand_chip)); | ||
| 102 | |||
| 103 | /* Link the private data with the MTD structure */ | ||
| 104 | omap_onenand_mtd->priv = this; | ||
| 105 | |||
| 106 | /* try the first address */ | ||
| 107 | this->base = ioremap(OMAP_ONENAND_FLASH_START1, SZ_128K); | ||
| 108 | omap_onenand_mtd->name = onenand_name; | ||
| 109 | if (onenand_scan(omap_onenand_mtd, 1)){ | ||
| 110 | /* try the second address */ | ||
| 111 | iounmap(this->base); | ||
| 112 | this->base = ioremap(OMAP_ONENAND_FLASH_START2, SZ_128K); | ||
| 113 | if (onenand_scan(omap_onenand_mtd, 1)) { | ||
| 114 | iounmap(this->base); | ||
| 115 | err = -ENXIO; | ||
| 116 | goto out_mtd; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Register the partitions */ | ||
| 121 | switch (omap_onenand_mtd->size) { | ||
| 122 | case SZ_128M: | ||
| 123 | case SZ_64M: | ||
| 124 | case SZ_32M: | ||
| 125 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 126 | err = parse_mtd_partitions(omap_onenand_mtd, part_probes, | ||
| 127 | &dynamic_partition, 0); | ||
| 128 | if (err > 0) | ||
| 129 | err = add_mtd_partitions(omap_onenand_mtd, | ||
| 130 | dynamic_partition, err); | ||
| 131 | else if (1) | ||
| 132 | err = add_mtd_partitions(omap_onenand_mtd, | ||
| 133 | static_partition, | ||
| 134 | ARRAY_SIZE(static_partition)); | ||
| 135 | else | ||
| 136 | #endif | ||
| 137 | err = add_mtd_device(omap_onenand_mtd); | ||
| 138 | if (err) | ||
| 139 | goto out_buf; | ||
| 140 | break; | ||
| 141 | |||
| 142 | default: | ||
| 143 | printk(KERN_WARNING "Unsupported OneNAND device\n"); | ||
| 144 | err = -ENXIO; | ||
| 145 | goto out_buf; | ||
| 146 | } | ||
| 147 | |||
| 148 | return 0; | ||
| 149 | |||
| 150 | out_buf: | ||
| 151 | onenand_release(omap_onenand_mtd); | ||
| 152 | iounmap(this->base); | ||
| 153 | out_mtd: | ||
| 154 | kfree(omap_onenand_mtd); | ||
| 155 | out: | ||
| 156 | return err; | ||
| 157 | } | ||
| 158 | |||
| 159 | /* | ||
| 160 | * Clean up routine | ||
| 161 | */ | ||
| 162 | static void __exit omap_onenand_cleanup (void) | ||
| 163 | { | ||
| 164 | struct onenand_chip *this = omap_onenand_mtd->priv; | ||
| 165 | |||
| 166 | /* onenand_release frees MTD partitions, MTD structure | ||
| 167 | and onenand internal buffers */ | ||
| 168 | onenand_release(omap_onenand_mtd); | ||
| 169 | iounmap(this->base); | ||
| 170 | kfree(omap_onenand_mtd); | ||
| 171 | } | ||
| 172 | |||
| 173 | module_init(omap_onenand_init); | ||
| 174 | module_exit(omap_onenand_cleanup); | ||
| 175 | |||
| 176 | MODULE_LICENSE("GPL"); | ||
| 177 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | ||
| 178 | MODULE_DESCRIPTION("Glue layer for OneNAND flash on OMAP boards"); | ||
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c new file mode 100644 index 000000000000..bcce22ae3cb1 --- /dev/null +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -0,0 +1,1462 @@ | |||
| 1 | /* | ||
| 2 | * linux/drivers/mtd/onenand/onenand_base.c | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Samsung Electronics | ||
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/init.h> | ||
| 15 | #include <linux/mtd/mtd.h> | ||
| 16 | #include <linux/mtd/onenand.h> | ||
| 17 | #include <linux/mtd/partitions.h> | ||
| 18 | |||
| 19 | #include <asm/io.h> | ||
| 20 | |||
| 21 | /** | ||
| 22 | * onenand_oob_64 - oob info for large (2KB) page | ||
| 23 | */ | ||
| 24 | static struct nand_oobinfo onenand_oob_64 = { | ||
| 25 | .useecc = MTD_NANDECC_AUTOPLACE, | ||
| 26 | .eccbytes = 20, | ||
| 27 | .eccpos = { | ||
| 28 | 8, 9, 10, 11, 12, | ||
| 29 | 24, 25, 26, 27, 28, | ||
| 30 | 40, 41, 42, 43, 44, | ||
| 31 | 56, 57, 58, 59, 60, | ||
| 32 | }, | ||
| 33 | .oobfree = { | ||
| 34 | {2, 3}, {14, 2}, {18, 3}, {30, 2}, | ||
| 35 | {24, 3}, {46, 2}, {40, 3}, {62, 2} } | ||
| 36 | }; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * onenand_oob_32 - oob info for middle (1KB) page | ||
| 40 | */ | ||
| 41 | static struct nand_oobinfo onenand_oob_32 = { | ||
| 42 | .useecc = MTD_NANDECC_AUTOPLACE, | ||
| 43 | .eccbytes = 10, | ||
| 44 | .eccpos = { | ||
| 45 | 8, 9, 10, 11, 12, | ||
| 46 | 24, 25, 26, 27, 28, | ||
| 47 | }, | ||
| 48 | .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} } | ||
| 49 | }; | ||
| 50 | |||
| 51 | static const unsigned char ffchars[] = { | ||
| 52 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 53 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ | ||
| 54 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 55 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ | ||
| 56 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 57 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ | ||
| 58 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
| 59 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * onenand_readw - [OneNAND Interface] Read OneNAND register | ||
| 64 | * @param addr address to read | ||
| 65 | * | ||
| 66 | * Read OneNAND register | ||
| 67 | */ | ||
| 68 | static unsigned short onenand_readw(void __iomem *addr) | ||
| 69 | { | ||
| 70 | return readw(addr); | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * onenand_writew - [OneNAND Interface] Write OneNAND register with value | ||
| 75 | * @param value value to write | ||
| 76 | * @param addr address to write | ||
| 77 | * | ||
| 78 | * Write OneNAND register with value | ||
| 79 | */ | ||
| 80 | static void onenand_writew(unsigned short value, void __iomem *addr) | ||
| 81 | { | ||
| 82 | writew(value, addr); | ||
| 83 | } | ||
| 84 | |||
| 85 | /** | ||
| 86 | * onenand_block_address - [DEFAULT] Get block address | ||
| 87 | * @param device the device id | ||
| 88 | * @param block the block | ||
| 89 | * @return translated block address if DDP, otherwise same | ||
| 90 | * | ||
| 91 | * Setup Start Address 1 Register (F100h) | ||
| 92 | */ | ||
| 93 | static int onenand_block_address(int device, int block) | ||
| 94 | { | ||
| 95 | if (device & ONENAND_DEVICE_IS_DDP) { | ||
| 96 | /* Device Flash Core select, NAND Flash Block Address */ | ||
| 97 | int dfs = 0, density, mask; | ||
| 98 | |||
| 99 | density = device >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
| 100 | mask = (1 << (density + 6)); | ||
| 101 | |||
| 102 | if (block & mask) | ||
| 103 | dfs = 1; | ||
| 104 | |||
| 105 | return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1)); | ||
| 106 | } | ||
| 107 | |||
| 108 | return block; | ||
| 109 | } | ||
| 110 | |||
| 111 | /** | ||
| 112 | * onenand_bufferram_address - [DEFAULT] Get bufferram address | ||
| 113 | * @param device the device id | ||
| 114 | * @param block the block | ||
| 115 | * @return set DBS value if DDP, otherwise 0 | ||
| 116 | * | ||
| 117 | * Setup Start Address 2 Register (F101h) for DDP | ||
| 118 | */ | ||
| 119 | static int onenand_bufferram_address(int device, int block) | ||
| 120 | { | ||
| 121 | if (device & ONENAND_DEVICE_IS_DDP) { | ||
| 122 | /* Device BufferRAM Select */ | ||
| 123 | int dbs = 0, density, mask; | ||
| 124 | |||
| 125 | density = device >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
| 126 | mask = (1 << (density + 6)); | ||
| 127 | |||
| 128 | if (block & mask) | ||
| 129 | dbs = 1; | ||
| 130 | |||
| 131 | return (dbs << ONENAND_DDP_SHIFT); | ||
| 132 | } | ||
| 133 | |||
| 134 | return 0; | ||
| 135 | } | ||
| 136 | |||
| 137 | /** | ||
| 138 | * onenand_page_address - [DEFAULT] Get page address | ||
| 139 | * @param page the page address | ||
| 140 | * @param sector the sector address | ||
| 141 | * @return combined page and sector address | ||
| 142 | * | ||
| 143 | * Setup Start Address 8 Register (F107h) | ||
| 144 | */ | ||
| 145 | static int onenand_page_address(int page, int sector) | ||
| 146 | { | ||
| 147 | /* Flash Page Address, Flash Sector Address */ | ||
| 148 | int fpa, fsa; | ||
| 149 | |||
| 150 | fpa = page & ONENAND_FPA_MASK; | ||
| 151 | fsa = sector & ONENAND_FSA_MASK; | ||
| 152 | |||
| 153 | return ((fpa << ONENAND_FPA_SHIFT) | fsa); | ||
| 154 | } | ||
| 155 | |||
| 156 | /** | ||
| 157 | * onenand_buffer_address - [DEFAULT] Get buffer address | ||
| 158 | * @param dataram1 DataRAM index | ||
| 159 | * @param sectors the sector address | ||
| 160 | * @param count the number of sectors | ||
| 161 | * @return the start buffer value | ||
| 162 | * | ||
| 163 | * Setup Start Buffer Register (F200h) | ||
| 164 | */ | ||
| 165 | static int onenand_buffer_address(int dataram1, int sectors, int count) | ||
| 166 | { | ||
| 167 | int bsa, bsc; | ||
| 168 | |||
| 169 | /* BufferRAM Sector Address */ | ||
| 170 | bsa = sectors & ONENAND_BSA_MASK; | ||
| 171 | |||
| 172 | if (dataram1) | ||
| 173 | bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ | ||
| 174 | else | ||
| 175 | bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ | ||
| 176 | |||
| 177 | /* BufferRAM Sector Count */ | ||
| 178 | bsc = count & ONENAND_BSC_MASK; | ||
| 179 | |||
| 180 | return ((bsa << ONENAND_BSA_SHIFT) | bsc); | ||
| 181 | } | ||
| 182 | |||
| 183 | /** | ||
| 184 | * onenand_command - [DEFAULT] Send command to OneNAND device | ||
| 185 | * @param mtd MTD device structure | ||
| 186 | * @param cmd the command to be sent | ||
| 187 | * @param addr offset to read from or write to | ||
| 188 | * @param len number of bytes to read or write | ||
| 189 | * | ||
| 190 | * Send command to OneNAND device. This function is used for middle/large page | ||
| 191 | * devices (1KB/2KB Bytes per page) | ||
| 192 | */ | ||
| 193 | static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) | ||
| 194 | { | ||
| 195 | struct onenand_chip *this = mtd->priv; | ||
| 196 | int value, readcmd = 0; | ||
| 197 | int block, page; | ||
| 198 | /* Now we use page size operation */ | ||
| 199 | int sectors = 4, count = 4; | ||
| 200 | |||
| 201 | /* Address translation */ | ||
| 202 | switch (cmd) { | ||
| 203 | case ONENAND_CMD_UNLOCK: | ||
| 204 | case ONENAND_CMD_LOCK: | ||
| 205 | case ONENAND_CMD_LOCK_TIGHT: | ||
| 206 | block = -1; | ||
| 207 | page = -1; | ||
| 208 | break; | ||
| 209 | |||
| 210 | case ONENAND_CMD_ERASE: | ||
| 211 | case ONENAND_CMD_BUFFERRAM: | ||
| 212 | block = (int) (addr >> this->erase_shift); | ||
| 213 | page = -1; | ||
| 214 | break; | ||
| 215 | |||
| 216 | default: | ||
| 217 | block = (int) (addr >> this->erase_shift); | ||
| 218 | page = (int) (addr >> this->page_shift); | ||
| 219 | page &= this->page_mask; | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* NOTE: The setting order of the registers is very important! */ | ||
| 224 | if (cmd == ONENAND_CMD_BUFFERRAM) { | ||
| 225 | /* Select DataRAM for DDP */ | ||
| 226 | value = onenand_bufferram_address(this->device_id, block); | ||
| 227 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | ||
| 228 | |||
| 229 | /* Switch to the next data buffer */ | ||
| 230 | ONENAND_SET_NEXT_BUFFERRAM(this); | ||
| 231 | |||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | if (block != -1) { | ||
| 236 | /* Write 'DFS, FBA' of Flash */ | ||
| 237 | value = onenand_block_address(this->device_id, block); | ||
| 238 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | ||
| 239 | } | ||
| 240 | |||
| 241 | if (page != -1) { | ||
| 242 | int dataram; | ||
| 243 | |||
| 244 | switch (cmd) { | ||
| 245 | case ONENAND_CMD_READ: | ||
| 246 | case ONENAND_CMD_READOOB: | ||
| 247 | dataram = ONENAND_SET_NEXT_BUFFERRAM(this); | ||
| 248 | readcmd = 1; | ||
| 249 | break; | ||
| 250 | |||
| 251 | default: | ||
| 252 | dataram = ONENAND_CURRENT_BUFFERRAM(this); | ||
| 253 | break; | ||
| 254 | } | ||
| 255 | |||
| 256 | /* Write 'FPA, FSA' of Flash */ | ||
| 257 | value = onenand_page_address(page, sectors); | ||
| 258 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8); | ||
| 259 | |||
| 260 | /* Write 'BSA, BSC' of DataRAM */ | ||
| 261 | value = onenand_buffer_address(dataram, sectors, count); | ||
| 262 | this->write_word(value, this->base + ONENAND_REG_START_BUFFER); | ||
| 263 | |||
| 264 | if (readcmd) { | ||
| 265 | /* Select DataRAM for DDP */ | ||
| 266 | value = onenand_bufferram_address(this->device_id, block); | ||
| 267 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | ||
| 268 | } | ||
| 269 | } | ||
| 270 | |||
| 271 | /* Interrupt clear */ | ||
| 272 | this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); | ||
| 273 | |||
| 274 | /* Write command */ | ||
| 275 | this->write_word(cmd, this->base + ONENAND_REG_COMMAND); | ||
| 276 | |||
| 277 | return 0; | ||
| 278 | } | ||
| 279 | |||
| 280 | /** | ||
| 281 | * onenand_wait - [DEFAULT] wait until the command is done | ||
| 282 | * @param mtd MTD device structure | ||
| 283 | * @param state state to select the max. timeout value | ||
| 284 | * | ||
| 285 | * Wait for command done. This applies to all OneNAND command | ||
| 286 | * Read can take up to 30us, erase up to 2ms and program up to 350us | ||
| 287 | * according to general OneNAND specs | ||
| 288 | */ | ||
| 289 | static int onenand_wait(struct mtd_info *mtd, int state) | ||
| 290 | { | ||
| 291 | struct onenand_chip * this = mtd->priv; | ||
| 292 | unsigned long timeout; | ||
| 293 | unsigned int flags = ONENAND_INT_MASTER; | ||
| 294 | unsigned int interrupt = 0; | ||
| 295 | unsigned int ctrl, ecc; | ||
| 296 | |||
| 297 | /* The 20 msec is enough */ | ||
| 298 | timeout = jiffies + msecs_to_jiffies(20); | ||
| 299 | while (time_before(jiffies, timeout)) { | ||
| 300 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
| 301 | |||
| 302 | if (interrupt & flags) | ||
| 303 | break; | ||
| 304 | |||
| 305 | if (state != FL_READING) | ||
| 306 | cond_resched(); | ||
| 307 | } | ||
| 308 | /* To get correct interrupt status in timeout case */ | ||
| 309 | interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); | ||
| 310 | |||
| 311 | ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); | ||
| 312 | |||
| 313 | if (ctrl & ONENAND_CTRL_ERROR) { | ||
| 314 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl); | ||
| 315 | return -EIO; | ||
| 316 | } | ||
| 317 | |||
| 318 | if (ctrl & ONENAND_CTRL_LOCK) { | ||
| 319 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl); | ||
| 320 | return -EIO; | ||
| 321 | } | ||
| 322 | |||
| 323 | if (interrupt & ONENAND_INT_READ) { | ||
| 324 | ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | ||
| 325 | if (ecc & ONENAND_ECC_2BIT_ALL) { | ||
| 326 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc); | ||
| 327 | return -EBADMSG; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | /** | ||
| 335 | * onenand_bufferram_offset - [DEFAULT] BufferRAM offset | ||
| 336 | * @param mtd MTD data structure | ||
| 337 | * @param area BufferRAM area | ||
| 338 | * @return offset given area | ||
| 339 | * | ||
| 340 | * Return BufferRAM offset given area | ||
| 341 | */ | ||
| 342 | static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) | ||
| 343 | { | ||
| 344 | struct onenand_chip *this = mtd->priv; | ||
| 345 | |||
| 346 | if (ONENAND_CURRENT_BUFFERRAM(this)) { | ||
| 347 | if (area == ONENAND_DATARAM) | ||
| 348 | return mtd->oobblock; | ||
| 349 | if (area == ONENAND_SPARERAM) | ||
| 350 | return mtd->oobsize; | ||
| 351 | } | ||
| 352 | |||
| 353 | return 0; | ||
| 354 | } | ||
| 355 | |||
| 356 | /** | ||
| 357 | * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area | ||
| 358 | * @param mtd MTD data structure | ||
| 359 | * @param area BufferRAM area | ||
| 360 | * @param buffer the databuffer to put/get data | ||
| 361 | * @param offset offset to read from or write to | ||
| 362 | * @param count number of bytes to read/write | ||
| 363 | * | ||
| 364 | * Read the BufferRAM area | ||
| 365 | */ | ||
| 366 | static int onenand_read_bufferram(struct mtd_info *mtd, int area, | ||
| 367 | unsigned char *buffer, int offset, size_t count) | ||
| 368 | { | ||
| 369 | struct onenand_chip *this = mtd->priv; | ||
| 370 | void __iomem *bufferram; | ||
| 371 | |||
| 372 | bufferram = this->base + area; | ||
| 373 | |||
| 374 | bufferram += onenand_bufferram_offset(mtd, area); | ||
| 375 | |||
| 376 | memcpy(buffer, bufferram + offset, count); | ||
| 377 | |||
| 378 | return 0; | ||
| 379 | } | ||
| 380 | |||
| 381 | /** | ||
| 382 | * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area | ||
| 383 | * @param mtd MTD data structure | ||
| 384 | * @param area BufferRAM area | ||
| 385 | * @param buffer the databuffer to put/get data | ||
| 386 | * @param offset offset to read from or write to | ||
| 387 | * @param count number of bytes to read/write | ||
| 388 | * | ||
| 389 | * Write the BufferRAM area | ||
| 390 | */ | ||
| 391 | static int onenand_write_bufferram(struct mtd_info *mtd, int area, | ||
| 392 | const unsigned char *buffer, int offset, size_t count) | ||
| 393 | { | ||
| 394 | struct onenand_chip *this = mtd->priv; | ||
| 395 | void __iomem *bufferram; | ||
| 396 | |||
| 397 | bufferram = this->base + area; | ||
| 398 | |||
| 399 | bufferram += onenand_bufferram_offset(mtd, area); | ||
| 400 | |||
| 401 | memcpy(bufferram + offset, buffer, count); | ||
| 402 | |||
| 403 | return 0; | ||
| 404 | } | ||
| 405 | |||
| 406 | /** | ||
| 407 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information | ||
| 408 | * @param mtd MTD data structure | ||
| 409 | * @param addr address to check | ||
| 410 | * @return 1 if there are valid data, otherwise 0 | ||
| 411 | * | ||
| 412 | * Check bufferram if there is data we required | ||
| 413 | */ | ||
| 414 | static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | ||
| 415 | { | ||
| 416 | struct onenand_chip *this = mtd->priv; | ||
| 417 | int block, page; | ||
| 418 | int i; | ||
| 419 | |||
| 420 | block = (int) (addr >> this->erase_shift); | ||
| 421 | page = (int) (addr >> this->page_shift); | ||
| 422 | page &= this->page_mask; | ||
| 423 | |||
| 424 | i = ONENAND_CURRENT_BUFFERRAM(this); | ||
| 425 | |||
| 426 | /* Is there valid data? */ | ||
| 427 | if (this->bufferram[i].block == block && | ||
| 428 | this->bufferram[i].page == page && | ||
| 429 | this->bufferram[i].valid) | ||
| 430 | return 1; | ||
| 431 | |||
| 432 | return 0; | ||
| 433 | } | ||
| 434 | |||
| 435 | /** | ||
| 436 | * onenand_update_bufferram - [GENERIC] Update BufferRAM information | ||
| 437 | * @param mtd MTD data structure | ||
| 438 | * @param addr address to update | ||
| 439 | * @param valid valid flag | ||
| 440 | * | ||
| 441 | * Update BufferRAM information | ||
| 442 | */ | ||
| 443 | static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | ||
| 444 | int valid) | ||
| 445 | { | ||
| 446 | struct onenand_chip *this = mtd->priv; | ||
| 447 | int block, page; | ||
| 448 | int i; | ||
| 449 | |||
| 450 | block = (int) (addr >> this->erase_shift); | ||
| 451 | page = (int) (addr >> this->page_shift); | ||
| 452 | page &= this->page_mask; | ||
| 453 | |||
| 454 | /* Invalidate BufferRAM */ | ||
| 455 | for (i = 0; i < MAX_BUFFERRAM; i++) { | ||
| 456 | if (this->bufferram[i].block == block && | ||
| 457 | this->bufferram[i].page == page) | ||
| 458 | this->bufferram[i].valid = 0; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* Update BufferRAM */ | ||
| 462 | i = ONENAND_CURRENT_BUFFERRAM(this); | ||
| 463 | this->bufferram[i].block = block; | ||
| 464 | this->bufferram[i].page = page; | ||
| 465 | this->bufferram[i].valid = valid; | ||
| 466 | |||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | /** | ||
| 471 | * onenand_get_device - [GENERIC] Get chip for selected access | ||
| 472 | * @param mtd MTD device structure | ||
| 473 | * @param new_state the state which is requested | ||
| 474 | * | ||
| 475 | * Get the device and lock it for exclusive access | ||
| 476 | */ | ||
| 477 | static void onenand_get_device(struct mtd_info *mtd, int new_state) | ||
| 478 | { | ||
| 479 | struct onenand_chip *this = mtd->priv; | ||
| 480 | DECLARE_WAITQUEUE(wait, current); | ||
| 481 | |||
| 482 | /* | ||
| 483 | * Grab the lock and see if the device is available | ||
| 484 | */ | ||
| 485 | while (1) { | ||
| 486 | spin_lock(&this->chip_lock); | ||
| 487 | if (this->state == FL_READY) { | ||
| 488 | this->state = new_state; | ||
| 489 | spin_unlock(&this->chip_lock); | ||
| 490 | break; | ||
| 491 | } | ||
| 492 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
| 493 | add_wait_queue(&this->wq, &wait); | ||
| 494 | spin_unlock(&this->chip_lock); | ||
| 495 | schedule(); | ||
| 496 | remove_wait_queue(&this->wq, &wait); | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | /** | ||
| 501 | * onenand_release_device - [GENERIC] release chip | ||
| 502 | * @param mtd MTD device structure | ||
| 503 | * | ||
| 504 | * Deselect, release chip lock and wake up anyone waiting on the device | ||
| 505 | */ | ||
| 506 | static void onenand_release_device(struct mtd_info *mtd) | ||
| 507 | { | ||
| 508 | struct onenand_chip *this = mtd->priv; | ||
| 509 | |||
| 510 | /* Release the chip */ | ||
| 511 | spin_lock(&this->chip_lock); | ||
| 512 | this->state = FL_READY; | ||
| 513 | wake_up(&this->wq); | ||
| 514 | spin_unlock(&this->chip_lock); | ||
| 515 | } | ||
| 516 | |||
| 517 | /** | ||
| 518 | * onenand_read_ecc - [MTD Interface] Read data with ECC | ||
| 519 | * @param mtd MTD device structure | ||
| 520 | * @param from offset to read from | ||
| 521 | * @param len number of bytes to read | ||
| 522 | * @param retlen pointer to variable to store the number of read bytes | ||
| 523 | * @param buf the databuffer to put data | ||
| 524 | * @param oob_buf filesystem supplied oob data buffer | ||
| 525 | * @param oobsel oob selection structure | ||
| 526 | * | ||
| 527 | * OneNAND read with ECC | ||
| 528 | */ | ||
| 529 | static int onenand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, | ||
| 530 | size_t *retlen, u_char *buf, | ||
| 531 | u_char *oob_buf, struct nand_oobinfo *oobsel) | ||
| 532 | { | ||
| 533 | struct onenand_chip *this = mtd->priv; | ||
| 534 | int read = 0, column; | ||
| 535 | int thislen; | ||
| 536 | int ret = 0; | ||
| 537 | |||
| 538 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
| 539 | |||
| 540 | /* Do not allow reads past end of device */ | ||
| 541 | if ((from + len) > mtd->size) { | ||
| 542 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: Attempt read beyond end of device\n"); | ||
| 543 | *retlen = 0; | ||
| 544 | return -EINVAL; | ||
| 545 | } | ||
| 546 | |||
| 547 | /* Grab the lock and see if the device is available */ | ||
| 548 | onenand_get_device(mtd, FL_READING); | ||
| 549 | |||
| 550 | /* TODO handling oob */ | ||
| 551 | |||
| 552 | while (read < len) { | ||
| 553 | thislen = min_t(int, mtd->oobblock, len - read); | ||
| 554 | |||
| 555 | column = from & (mtd->oobblock - 1); | ||
| 556 | if (column + thislen > mtd->oobblock) | ||
| 557 | thislen = mtd->oobblock - column; | ||
| 558 | |||
| 559 | if (!onenand_check_bufferram(mtd, from)) { | ||
| 560 | this->command(mtd, ONENAND_CMD_READ, from, mtd->oobblock); | ||
| 561 | |||
| 562 | ret = this->wait(mtd, FL_READING); | ||
| 563 | /* First copy data and check return value for ECC handling */ | ||
| 564 | onenand_update_bufferram(mtd, from, 1); | ||
| 565 | } | ||
| 566 | |||
| 567 | this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); | ||
| 568 | |||
| 569 | read += thislen; | ||
| 570 | |||
| 571 | if (read == len) | ||
| 572 | break; | ||
| 573 | |||
| 574 | if (ret) { | ||
| 575 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_ecc: read failed = %d\n", ret); | ||
| 576 | goto out; | ||
| 577 | } | ||
| 578 | |||
| 579 | from += thislen; | ||
| 580 | buf += thislen; | ||
| 581 | } | ||
| 582 | |||
| 583 | out: | ||
| 584 | /* Deselect and wake up anyone waiting on the device */ | ||
| 585 | onenand_release_device(mtd); | ||
| 586 | |||
| 587 | /* | ||
| 588 | * Return success, if no ECC failures, else -EBADMSG | ||
| 589 | * fs driver will take care of that, because | ||
| 590 | * retlen == desired len and result == -EBADMSG | ||
| 591 | */ | ||
| 592 | *retlen = read; | ||
| 593 | return ret; | ||
| 594 | } | ||
| 595 | |||
| 596 | /** | ||
| 597 | * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc | ||
| 598 | * @param mtd MTD device structure | ||
| 599 | * @param from offset to read from | ||
| 600 | * @param len number of bytes to read | ||
| 601 | * @param retlen pointer to variable to store the number of read bytes | ||
| 602 | * @param buf the databuffer to put data | ||
| 603 | * | ||
| 604 | * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL | ||
| 605 | */ | ||
| 606 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
| 607 | size_t *retlen, u_char *buf) | ||
| 608 | { | ||
| 609 | return onenand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); | ||
| 610 | } | ||
| 611 | |||
| 612 | /** | ||
| 613 | * onenand_read_oob - [MTD Interface] OneNAND read out-of-band | ||
| 614 | * @param mtd MTD device structure | ||
| 615 | * @param from offset to read from | ||
| 616 | * @param len number of bytes to read | ||
| 617 | * @param retlen pointer to variable to store the number of read bytes | ||
| 618 | * @param buf the databuffer to put data | ||
| 619 | * | ||
| 620 | * OneNAND read out-of-band data from the spare area | ||
| 621 | */ | ||
| 622 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | ||
| 623 | size_t *retlen, u_char *buf) | ||
| 624 | { | ||
| 625 | struct onenand_chip *this = mtd->priv; | ||
| 626 | int read = 0, thislen, column; | ||
| 627 | int ret = 0; | ||
| 628 | |||
| 629 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
| 630 | |||
| 631 | /* Initialize return length value */ | ||
| 632 | *retlen = 0; | ||
| 633 | |||
| 634 | /* Do not allow reads past end of device */ | ||
| 635 | if (unlikely((from + len) > mtd->size)) { | ||
| 636 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); | ||
| 637 | return -EINVAL; | ||
| 638 | } | ||
| 639 | |||
| 640 | /* Grab the lock and see if the device is available */ | ||
| 641 | onenand_get_device(mtd, FL_READING); | ||
| 642 | |||
| 643 | column = from & (mtd->oobsize - 1); | ||
| 644 | |||
| 645 | while (read < len) { | ||
| 646 | thislen = mtd->oobsize - column; | ||
| 647 | thislen = min_t(int, thislen, len); | ||
| 648 | |||
| 649 | this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); | ||
| 650 | |||
| 651 | onenand_update_bufferram(mtd, from, 0); | ||
| 652 | |||
| 653 | ret = this->wait(mtd, FL_READING); | ||
| 654 | /* First copy data and check return value for ECC handling */ | ||
| 655 | |||
| 656 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
| 657 | |||
| 658 | read += thislen; | ||
| 659 | |||
| 660 | if (read == len) | ||
| 661 | break; | ||
| 662 | |||
| 663 | if (ret) { | ||
| 664 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = %d\n", ret); | ||
| 665 | goto out; | ||
| 666 | } | ||
| 667 | |||
| 668 | buf += thislen; | ||
| 669 | |||
| 670 | /* Read more? */ | ||
| 671 | if (read < len) { | ||
| 672 | /* Page size */ | ||
| 673 | from += mtd->oobblock; | ||
| 674 | column = 0; | ||
| 675 | } | ||
| 676 | } | ||
| 677 | |||
| 678 | out: | ||
| 679 | /* Deselect and wake up anyone waiting on the device */ | ||
| 680 | onenand_release_device(mtd); | ||
| 681 | |||
| 682 | *retlen = read; | ||
| 683 | return ret; | ||
| 684 | } | ||
| 685 | |||
| 686 | #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE | ||
| 687 | /** | ||
| 688 | * onenand_verify_page - [GENERIC] verify the chip contents after a write | ||
| 689 | * @param mtd MTD device structure | ||
| 690 | * @param buf the databuffer to verify | ||
| 691 | * @param block block address | ||
| 692 | * @param page page address | ||
| 693 | * | ||
| 694 | * Check DataRAM area directly | ||
| 695 | */ | ||
| 696 | static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, | ||
| 697 | loff_t addr, int block, int page) | ||
| 698 | { | ||
| 699 | struct onenand_chip *this = mtd->priv; | ||
| 700 | void __iomem *dataram0, *dataram1; | ||
| 701 | int ret = 0; | ||
| 702 | |||
| 703 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->oobblock); | ||
| 704 | |||
| 705 | ret = this->wait(mtd, FL_READING); | ||
| 706 | if (ret) | ||
| 707 | return ret; | ||
| 708 | |||
| 709 | onenand_update_bufferram(mtd, addr, 1); | ||
| 710 | |||
| 711 | /* Check, if the two dataram areas are same */ | ||
| 712 | dataram0 = this->base + ONENAND_DATARAM; | ||
| 713 | dataram1 = dataram0 + mtd->oobblock; | ||
| 714 | |||
| 715 | if (memcmp(dataram0, dataram1, mtd->oobblock)) | ||
| 716 | return -EBADMSG; | ||
| 717 | |||
| 718 | return 0; | ||
| 719 | } | ||
| 720 | #else | ||
| 721 | #define onenand_verify_page(...) (0) | ||
| 722 | #endif | ||
| 723 | |||
| 724 | #define NOTALIGNED(x) ((x & (mtd->oobblock - 1)) != 0) | ||
| 725 | |||
| 726 | /** | ||
| 727 | * onenand_write_ecc - [MTD Interface] OneNAND write with ECC | ||
| 728 | * @param mtd MTD device structure | ||
| 729 | * @param to offset to write to | ||
| 730 | * @param len number of bytes to write | ||
| 731 | * @param retlen pointer to variable to store the number of written bytes | ||
| 732 | * @param buf the data to write | ||
| 733 | * @param eccbuf filesystem supplied oob data buffer | ||
| 734 | * @param oobsel oob selection structure | ||
| 735 | * | ||
| 736 | * OneNAND write with ECC | ||
| 737 | */ | ||
| 738 | static int onenand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 739 | size_t *retlen, const u_char *buf, | ||
| 740 | u_char *eccbuf, struct nand_oobinfo *oobsel) | ||
| 741 | { | ||
| 742 | struct onenand_chip *this = mtd->priv; | ||
| 743 | int written = 0; | ||
| 744 | int ret = 0; | ||
| 745 | |||
| 746 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | ||
| 747 | |||
| 748 | /* Initialize retlen, in case of early exit */ | ||
| 749 | *retlen = 0; | ||
| 750 | |||
| 751 | /* Do not allow writes past end of device */ | ||
| 752 | if (unlikely((to + len) > mtd->size)) { | ||
| 753 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt write to past end of device\n"); | ||
| 754 | return -EINVAL; | ||
| 755 | } | ||
| 756 | |||
| 757 | /* Reject writes, which are not page aligned */ | ||
| 758 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | ||
| 759 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: Attempt to write not page aligned data\n"); | ||
| 760 | return -EINVAL; | ||
| 761 | } | ||
| 762 | |||
| 763 | /* Grab the lock and see if the device is available */ | ||
| 764 | onenand_get_device(mtd, FL_WRITING); | ||
| 765 | |||
| 766 | /* Loop until all data write */ | ||
| 767 | while (written < len) { | ||
| 768 | int thislen = min_t(int, mtd->oobblock, len - written); | ||
| 769 | |||
| 770 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); | ||
| 771 | |||
| 772 | this->write_bufferram(mtd, ONENAND_DATARAM, buf, 0, thislen); | ||
| 773 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
| 774 | |||
| 775 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); | ||
| 776 | |||
| 777 | onenand_update_bufferram(mtd, to, 1); | ||
| 778 | |||
| 779 | ret = this->wait(mtd, FL_WRITING); | ||
| 780 | if (ret) { | ||
| 781 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: write filaed %d\n", ret); | ||
| 782 | goto out; | ||
| 783 | } | ||
| 784 | |||
| 785 | written += thislen; | ||
| 786 | |||
| 787 | /* Only check verify write turn on */ | ||
| 788 | ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page); | ||
| 789 | if (ret) { | ||
| 790 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret); | ||
| 791 | goto out; | ||
| 792 | } | ||
| 793 | |||
| 794 | if (written == len) | ||
| 795 | break; | ||
| 796 | |||
| 797 | to += thislen; | ||
| 798 | buf += thislen; | ||
| 799 | } | ||
| 800 | |||
| 801 | out: | ||
| 802 | /* Deselect and wake up anyone waiting on the device */ | ||
| 803 | onenand_release_device(mtd); | ||
| 804 | |||
| 805 | *retlen = written; | ||
| 806 | |||
| 807 | return ret; | ||
| 808 | } | ||
| 809 | |||
| 810 | /** | ||
| 811 | * onenand_write - [MTD Interface] compability function for onenand_write_ecc | ||
| 812 | * @param mtd MTD device structure | ||
| 813 | * @param to offset to write to | ||
| 814 | * @param len number of bytes to write | ||
| 815 | * @param retlen pointer to variable to store the number of written bytes | ||
| 816 | * @param buf the data to write | ||
| 817 | * | ||
| 818 | * This function simply calls onenand_write_ecc | ||
| 819 | * with oob buffer and oobsel = NULL | ||
| 820 | */ | ||
| 821 | static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 822 | size_t *retlen, const u_char *buf) | ||
| 823 | { | ||
| 824 | return onenand_write_ecc(mtd, to, len, retlen, buf, NULL, NULL); | ||
| 825 | } | ||
| 826 | |||
| 827 | /** | ||
| 828 | * onenand_write_oob - [MTD Interface] OneNAND write out-of-band | ||
| 829 | * @param mtd MTD device structure | ||
| 830 | * @param to offset to write to | ||
| 831 | * @param len number of bytes to write | ||
| 832 | * @param retlen pointer to variable to store the number of written bytes | ||
| 833 | * @param buf the data to write | ||
| 834 | * | ||
| 835 | * OneNAND write out-of-band | ||
| 836 | */ | ||
| 837 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | ||
| 838 | size_t *retlen, const u_char *buf) | ||
| 839 | { | ||
| 840 | struct onenand_chip *this = mtd->priv; | ||
| 841 | int column, status; | ||
| 842 | int written = 0; | ||
| 843 | |||
| 844 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | ||
| 845 | |||
| 846 | /* Initialize retlen, in case of early exit */ | ||
| 847 | *retlen = 0; | ||
| 848 | |||
| 849 | /* Do not allow writes past end of device */ | ||
| 850 | if (unlikely((to + len) > mtd->size)) { | ||
| 851 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); | ||
| 852 | return -EINVAL; | ||
| 853 | } | ||
| 854 | |||
| 855 | /* Grab the lock and see if the device is available */ | ||
| 856 | onenand_get_device(mtd, FL_WRITING); | ||
| 857 | |||
| 858 | /* Loop until all data write */ | ||
| 859 | while (written < len) { | ||
| 860 | int thislen = min_t(int, mtd->oobsize, len - written); | ||
| 861 | |||
| 862 | column = to & (mtd->oobsize - 1); | ||
| 863 | |||
| 864 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); | ||
| 865 | |||
| 866 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
| 867 | this->write_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | ||
| 868 | |||
| 869 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | ||
| 870 | |||
| 871 | onenand_update_bufferram(mtd, to, 0); | ||
| 872 | |||
| 873 | status = this->wait(mtd, FL_WRITING); | ||
| 874 | if (status) | ||
| 875 | goto out; | ||
| 876 | |||
| 877 | written += thislen; | ||
| 878 | |||
| 879 | if (written == len) | ||
| 880 | break; | ||
| 881 | |||
| 882 | to += thislen; | ||
| 883 | buf += thislen; | ||
| 884 | } | ||
| 885 | |||
| 886 | out: | ||
| 887 | /* Deselect and wake up anyone waiting on the device */ | ||
| 888 | onenand_release_device(mtd); | ||
| 889 | |||
| 890 | *retlen = written; | ||
| 891 | |||
| 892 | return 0; | ||
| 893 | } | ||
| 894 | |||
| 895 | /** | ||
| 896 | * onenand_writev_ecc - [MTD Interface] write with iovec with ecc | ||
| 897 | * @param mtd MTD device structure | ||
| 898 | * @param vecs the iovectors to write | ||
| 899 | * @param count number of vectors | ||
| 900 | * @param to offset to write to | ||
| 901 | * @param retlen pointer to variable to store the number of written bytes | ||
| 902 | * @param eccbuf filesystem supplied oob data buffer | ||
| 903 | * @param oobsel oob selection structure | ||
| 904 | * | ||
| 905 | * OneNAND write with iovec with ecc | ||
| 906 | */ | ||
| 907 | static int onenand_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, | ||
| 908 | unsigned long count, loff_t to, size_t *retlen, | ||
| 909 | u_char *eccbuf, struct nand_oobinfo *oobsel) | ||
| 910 | { | ||
| 911 | struct onenand_chip *this = mtd->priv; | ||
| 912 | unsigned char buffer[mtd->oobblock], *pbuf; | ||
| 913 | size_t total_len, len; | ||
| 914 | int i, written = 0; | ||
| 915 | int ret = 0; | ||
| 916 | |||
| 917 | /* Preset written len for early exit */ | ||
| 918 | *retlen = 0; | ||
| 919 | |||
| 920 | /* Calculate total length of data */ | ||
| 921 | total_len = 0; | ||
| 922 | for (i = 0; i < count; i++) | ||
| 923 | total_len += vecs[i].iov_len; | ||
| 924 | |||
| 925 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_writev_ecc: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); | ||
| 926 | |||
| 927 | /* Do not allow write past end of the device */ | ||
| 928 | if (unlikely((to + total_len) > mtd->size)) { | ||
| 929 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempted write past end of device\n"); | ||
| 930 | return -EINVAL; | ||
| 931 | } | ||
| 932 | |||
| 933 | /* Reject writes, which are not page aligned */ | ||
| 934 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(total_len))) { | ||
| 935 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: Attempt to write not page aligned data\n"); | ||
| 936 | return -EINVAL; | ||
| 937 | } | ||
| 938 | |||
| 939 | /* Grab the lock and see if the device is available */ | ||
| 940 | onenand_get_device(mtd, FL_WRITING); | ||
| 941 | |||
| 942 | /* TODO handling oob */ | ||
| 943 | |||
| 944 | /* Loop until all keve's data has been written */ | ||
| 945 | len = 0; | ||
| 946 | while (count) { | ||
| 947 | pbuf = buffer; | ||
| 948 | /* | ||
| 949 | * If the given tuple is >= pagesize then | ||
| 950 | * write it out from the iov | ||
| 951 | */ | ||
| 952 | if ((vecs->iov_len - len) >= mtd->oobblock) { | ||
| 953 | pbuf = vecs->iov_base + len; | ||
| 954 | |||
| 955 | len += mtd->oobblock; | ||
| 956 | |||
| 957 | /* Check, if we have to switch to the next tuple */ | ||
| 958 | if (len >= (int) vecs->iov_len) { | ||
| 959 | vecs++; | ||
| 960 | len = 0; | ||
| 961 | count--; | ||
| 962 | } | ||
| 963 | } else { | ||
| 964 | int cnt = 0, thislen; | ||
| 965 | while (cnt < mtd->oobblock) { | ||
| 966 | thislen = min_t(int, mtd->oobblock - cnt, vecs->iov_len - len); | ||
| 967 | memcpy(buffer + cnt, vecs->iov_base + len, thislen); | ||
| 968 | cnt += thislen; | ||
| 969 | len += thislen; | ||
| 970 | |||
| 971 | /* Check, if we have to switch to the next tuple */ | ||
| 972 | if (len >= (int) vecs->iov_len) { | ||
| 973 | vecs++; | ||
| 974 | len = 0; | ||
| 975 | count--; | ||
| 976 | } | ||
| 977 | } | ||
| 978 | } | ||
| 979 | |||
| 980 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobblock); | ||
| 981 | |||
| 982 | this->write_bufferram(mtd, ONENAND_DATARAM, pbuf, 0, mtd->oobblock); | ||
| 983 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | ||
| 984 | |||
| 985 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->oobblock); | ||
| 986 | |||
| 987 | onenand_update_bufferram(mtd, to, 1); | ||
| 988 | |||
| 989 | ret = this->wait(mtd, FL_WRITING); | ||
| 990 | if (ret) { | ||
| 991 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: write failed %d\n", ret); | ||
| 992 | goto out; | ||
| 993 | } | ||
| 994 | |||
| 995 | |||
| 996 | /* Only check verify write turn on */ | ||
| 997 | ret = onenand_verify_page(mtd, (u_char *) pbuf, to, block, page); | ||
| 998 | if (ret) { | ||
| 999 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret); | ||
| 1000 | goto out; | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | written += mtd->oobblock; | ||
| 1004 | |||
| 1005 | to += mtd->oobblock; | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | out: | ||
| 1009 | /* Deselect and wakt up anyone waiting on the device */ | ||
| 1010 | onenand_release_device(mtd); | ||
| 1011 | |||
| 1012 | *retlen = written; | ||
| 1013 | |||
| 1014 | return 0; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | /** | ||
| 1018 | * onenand_writev - [MTD Interface] compabilty function for onenand_writev_ecc | ||
| 1019 | * @param mtd MTD device structure | ||
| 1020 | * @param vecs the iovectors to write | ||
| 1021 | * @param count number of vectors | ||
| 1022 | * @param to offset to write to | ||
| 1023 | * @param retlen pointer to variable to store the number of written bytes | ||
| 1024 | * | ||
| 1025 | * OneNAND write with kvec. This just calls the ecc function | ||
| 1026 | */ | ||
| 1027 | static int onenand_writev(struct mtd_info *mtd, const struct kvec *vecs, | ||
| 1028 | unsigned long count, loff_t to, size_t *retlen) | ||
| 1029 | { | ||
| 1030 | return onenand_writev_ecc(mtd, vecs, count, to, retlen, NULL, NULL); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | /** | ||
| 1034 | * onenand_erase - [MTD Interface] erase block(s) | ||
| 1035 | * @param mtd MTD device structure | ||
| 1036 | * @param instr erase instruction | ||
| 1037 | * | ||
| 1038 | * Erase one ore more blocks | ||
| 1039 | */ | ||
| 1040 | static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
| 1041 | { | ||
| 1042 | struct onenand_chip *this = mtd->priv; | ||
| 1043 | unsigned int block_size; | ||
| 1044 | loff_t addr; | ||
| 1045 | int len; | ||
| 1046 | int ret = 0; | ||
| 1047 | |||
| 1048 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); | ||
| 1049 | |||
| 1050 | block_size = (1 << this->erase_shift); | ||
| 1051 | |||
| 1052 | /* Start address must align on block boundary */ | ||
| 1053 | if (unlikely(instr->addr & (block_size - 1))) { | ||
| 1054 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); | ||
| 1055 | return -EINVAL; | ||
| 1056 | } | ||
| 1057 | |||
| 1058 | /* Length must align on block boundary */ | ||
| 1059 | if (unlikely(instr->len & (block_size - 1))) { | ||
| 1060 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); | ||
| 1061 | return -EINVAL; | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | /* Do not allow erase past end of device */ | ||
| 1065 | if (unlikely((instr->len + instr->addr) > mtd->size)) { | ||
| 1066 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); | ||
| 1067 | return -EINVAL; | ||
| 1068 | } | ||
| 1069 | |||
| 1070 | instr->fail_addr = 0xffffffff; | ||
| 1071 | |||
| 1072 | /* Grab the lock and see if the device is available */ | ||
| 1073 | onenand_get_device(mtd, FL_ERASING); | ||
| 1074 | |||
| 1075 | /* Loop throught the pages */ | ||
| 1076 | len = instr->len; | ||
| 1077 | addr = instr->addr; | ||
| 1078 | |||
| 1079 | instr->state = MTD_ERASING; | ||
| 1080 | |||
| 1081 | while (len) { | ||
| 1082 | |||
| 1083 | /* TODO Check badblock */ | ||
| 1084 | |||
| 1085 | this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); | ||
| 1086 | |||
| 1087 | ret = this->wait(mtd, FL_ERASING); | ||
| 1088 | /* Check, if it is write protected */ | ||
| 1089 | if (ret) { | ||
| 1090 | if (ret == -EPERM) | ||
| 1091 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Device is write protected!!!\n"); | ||
| 1092 | else | ||
| 1093 | DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); | ||
| 1094 | instr->state = MTD_ERASE_FAILED; | ||
| 1095 | instr->fail_addr = addr; | ||
| 1096 | goto erase_exit; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | len -= block_size; | ||
| 1100 | addr += block_size; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | instr->state = MTD_ERASE_DONE; | ||
| 1104 | |||
| 1105 | erase_exit: | ||
| 1106 | |||
| 1107 | ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; | ||
| 1108 | /* Do call back function */ | ||
| 1109 | if (!ret) | ||
| 1110 | mtd_erase_callback(instr); | ||
| 1111 | |||
| 1112 | /* Deselect and wake up anyone waiting on the device */ | ||
| 1113 | onenand_release_device(mtd); | ||
| 1114 | |||
| 1115 | return ret; | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | /** | ||
| 1119 | * onenand_sync - [MTD Interface] sync | ||
| 1120 | * @param mtd MTD device structure | ||
| 1121 | * | ||
| 1122 | * Sync is actually a wait for chip ready function | ||
| 1123 | */ | ||
| 1124 | static void onenand_sync(struct mtd_info *mtd) | ||
| 1125 | { | ||
| 1126 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); | ||
| 1127 | |||
| 1128 | /* Grab the lock and see if the device is available */ | ||
| 1129 | onenand_get_device(mtd, FL_SYNCING); | ||
| 1130 | |||
| 1131 | /* Release it and go back */ | ||
| 1132 | onenand_release_device(mtd); | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | /** | ||
| 1136 | * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad | ||
| 1137 | * @param mtd MTD device structure | ||
| 1138 | * @param ofs offset relative to mtd start | ||
| 1139 | */ | ||
| 1140 | static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) | ||
| 1141 | { | ||
| 1142 | /* | ||
| 1143 | * TODO | ||
| 1144 | * 1. Bad block table (BBT) | ||
| 1145 | * -> using NAND BBT to support JFFS2 | ||
| 1146 | * 2. Bad block management (BBM) | ||
| 1147 | * -> bad block replace scheme | ||
| 1148 | * | ||
| 1149 | * Currently we do nothing | ||
| 1150 | */ | ||
| 1151 | return 0; | ||
| 1152 | } | ||
| 1153 | |||
| 1154 | /** | ||
| 1155 | * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad | ||
| 1156 | * @param mtd MTD device structure | ||
| 1157 | * @param ofs offset relative to mtd start | ||
| 1158 | */ | ||
| 1159 | static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) | ||
| 1160 | { | ||
| 1161 | /* see above */ | ||
| 1162 | return 0; | ||
| 1163 | } | ||
| 1164 | |||
| 1165 | /** | ||
| 1166 | * onenand_unlock - [MTD Interface] Unlock block(s) | ||
| 1167 | * @param mtd MTD device structure | ||
| 1168 | * @param ofs offset relative to mtd start | ||
| 1169 | * @param len number of bytes to unlock | ||
| 1170 | * | ||
| 1171 | * Unlock one or more blocks | ||
| 1172 | */ | ||
| 1173 | static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | ||
| 1174 | { | ||
| 1175 | struct onenand_chip *this = mtd->priv; | ||
| 1176 | int start, end, block, value, status; | ||
| 1177 | |||
| 1178 | start = ofs >> this->erase_shift; | ||
| 1179 | end = len >> this->erase_shift; | ||
| 1180 | |||
| 1181 | /* Continuous lock scheme */ | ||
| 1182 | if (this->options & ONENAND_CONT_LOCK) { | ||
| 1183 | /* Set start block address */ | ||
| 1184 | this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
| 1185 | /* Set end block address */ | ||
| 1186 | this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); | ||
| 1187 | /* Write unlock command */ | ||
| 1188 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | ||
| 1189 | |||
| 1190 | /* There's no return value */ | ||
| 1191 | this->wait(mtd, FL_UNLOCKING); | ||
| 1192 | |||
| 1193 | /* Sanity check */ | ||
| 1194 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | ||
| 1195 | & ONENAND_CTRL_ONGO) | ||
| 1196 | continue; | ||
| 1197 | |||
| 1198 | /* Check lock status */ | ||
| 1199 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | ||
| 1200 | if (!(status & ONENAND_WP_US)) | ||
| 1201 | printk(KERN_ERR "wp status = 0x%x\n", status); | ||
| 1202 | |||
| 1203 | return 0; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | /* Block lock scheme */ | ||
| 1207 | for (block = start; block < end; block++) { | ||
| 1208 | /* Set start block address */ | ||
| 1209 | this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); | ||
| 1210 | /* Write unlock command */ | ||
| 1211 | this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); | ||
| 1212 | |||
| 1213 | /* There's no return value */ | ||
| 1214 | this->wait(mtd, FL_UNLOCKING); | ||
| 1215 | |||
| 1216 | /* Sanity check */ | ||
| 1217 | while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) | ||
| 1218 | & ONENAND_CTRL_ONGO) | ||
| 1219 | continue; | ||
| 1220 | |||
| 1221 | /* Set block address for read block status */ | ||
| 1222 | value = onenand_block_address(this->device_id, block); | ||
| 1223 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); | ||
| 1224 | |||
| 1225 | /* Check lock status */ | ||
| 1226 | status = this->read_word(this->base + ONENAND_REG_WP_STATUS); | ||
| 1227 | if (!(status & ONENAND_WP_US)) | ||
| 1228 | printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); | ||
| 1229 | } | ||
| 1230 | |||
| 1231 | return 0; | ||
| 1232 | } | ||
| 1233 | |||
| 1234 | /** | ||
| 1235 | * onenand_print_device_info - Print device ID | ||
| 1236 | * @param device device ID | ||
| 1237 | * | ||
| 1238 | * Print device ID | ||
| 1239 | */ | ||
| 1240 | static void onenand_print_device_info(int device) | ||
| 1241 | { | ||
| 1242 | int vcc, demuxed, ddp, density; | ||
| 1243 | |||
| 1244 | vcc = device & ONENAND_DEVICE_VCC_MASK; | ||
| 1245 | demuxed = device & ONENAND_DEVICE_IS_DEMUX; | ||
| 1246 | ddp = device & ONENAND_DEVICE_IS_DDP; | ||
| 1247 | density = device >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
| 1248 | printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", | ||
| 1249 | demuxed ? "" : "Muxed ", | ||
| 1250 | ddp ? "(DDP)" : "", | ||
| 1251 | (16 << density), | ||
| 1252 | vcc ? "2.65/3.3" : "1.8", | ||
| 1253 | device); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | static const struct onenand_manufacturers onenand_manuf_ids[] = { | ||
| 1257 | {ONENAND_MFR_SAMSUNG, "Samsung"}, | ||
| 1258 | {ONENAND_MFR_UNKNOWN, "Unknown"} | ||
| 1259 | }; | ||
| 1260 | |||
| 1261 | /** | ||
| 1262 | * onenand_check_maf - Check manufacturer ID | ||
| 1263 | * @param manuf manufacturer ID | ||
| 1264 | * | ||
| 1265 | * Check manufacturer ID | ||
| 1266 | */ | ||
| 1267 | static int onenand_check_maf(int manuf) | ||
| 1268 | { | ||
| 1269 | int i; | ||
| 1270 | |||
| 1271 | for (i = 0; onenand_manuf_ids[i].id; i++) { | ||
| 1272 | if (manuf == onenand_manuf_ids[i].id) | ||
| 1273 | break; | ||
| 1274 | } | ||
| 1275 | |||
| 1276 | printk(KERN_DEBUG "OneNAND Manufacturer: %s\n", | ||
| 1277 | onenand_manuf_ids[i].name); | ||
| 1278 | |||
| 1279 | return (i != ONENAND_MFR_UNKNOWN); | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | /** | ||
| 1283 | * onenand_probe - [OneNAND Interface] Probe the OneNAND device | ||
| 1284 | * @param mtd MTD device structure | ||
| 1285 | * | ||
| 1286 | * OneNAND detection method: | ||
| 1287 | * Compare the the values from command with ones from register | ||
| 1288 | */ | ||
| 1289 | static int onenand_probe(struct mtd_info *mtd) | ||
| 1290 | { | ||
| 1291 | struct onenand_chip *this = mtd->priv; | ||
| 1292 | int bram_maf_id, bram_dev_id, maf_id, dev_id; | ||
| 1293 | int version_id; | ||
| 1294 | int density; | ||
| 1295 | |||
| 1296 | /* Send the command for reading device ID from BootRAM */ | ||
| 1297 | this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); | ||
| 1298 | |||
| 1299 | /* Read manufacturer and device IDs from BootRAM */ | ||
| 1300 | bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); | ||
| 1301 | bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); | ||
| 1302 | |||
| 1303 | /* Check manufacturer ID */ | ||
| 1304 | if (onenand_check_maf(bram_maf_id)) | ||
| 1305 | return -ENXIO; | ||
| 1306 | |||
| 1307 | /* Reset OneNAND to read default register values */ | ||
| 1308 | this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); | ||
| 1309 | |||
| 1310 | /* Read manufacturer and device IDs from Register */ | ||
| 1311 | maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); | ||
| 1312 | dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); | ||
| 1313 | |||
| 1314 | /* Check OneNAND device */ | ||
| 1315 | if (maf_id != bram_maf_id || dev_id != bram_dev_id) | ||
| 1316 | return -ENXIO; | ||
| 1317 | |||
| 1318 | /* Flash device information */ | ||
| 1319 | onenand_print_device_info(dev_id); | ||
| 1320 | this->device_id = dev_id; | ||
| 1321 | |||
| 1322 | density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
| 1323 | this->chipsize = (16 << density) << 20; | ||
| 1324 | |||
| 1325 | /* OneNAND page size & block size */ | ||
| 1326 | /* The data buffer size is equal to page size */ | ||
| 1327 | mtd->oobblock = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); | ||
| 1328 | mtd->oobsize = mtd->oobblock >> 5; | ||
| 1329 | /* Pagers per block is always 64 in OneNAND */ | ||
| 1330 | mtd->erasesize = mtd->oobblock << 6; | ||
| 1331 | |||
| 1332 | this->erase_shift = ffs(mtd->erasesize) - 1; | ||
| 1333 | this->page_shift = ffs(mtd->oobblock) - 1; | ||
| 1334 | this->ppb_shift = (this->erase_shift - this->page_shift); | ||
| 1335 | this->page_mask = (mtd->erasesize / mtd->oobblock) - 1; | ||
| 1336 | |||
| 1337 | /* REVIST: Multichip handling */ | ||
| 1338 | |||
| 1339 | mtd->size = this->chipsize; | ||
| 1340 | |||
| 1341 | /* Version ID */ | ||
| 1342 | version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); | ||
| 1343 | printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); | ||
| 1344 | |||
| 1345 | /* Lock scheme */ | ||
| 1346 | if (density <= ONENAND_DEVICE_DENSITY_512Mb && | ||
| 1347 | !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { | ||
| 1348 | printk(KERN_INFO "Lock scheme is Continues Lock\n"); | ||
| 1349 | this->options |= ONENAND_CONT_LOCK; | ||
| 1350 | } | ||
| 1351 | |||
| 1352 | return 0; | ||
| 1353 | } | ||
| 1354 | |||
| 1355 | |||
| 1356 | /** | ||
| 1357 | * onenand_scan - [OneNAND Interface] Scan for the OneNAND device | ||
| 1358 | * @param mtd MTD device structure | ||
| 1359 | * @param maxchips Number of chips to scan for | ||
| 1360 | * | ||
| 1361 | * This fills out all the not initialized function pointers | ||
| 1362 | * with the defaults. | ||
| 1363 | * The flash ID is read and the mtd/chip structures are | ||
| 1364 | * filled with the appropriate values. | ||
| 1365 | */ | ||
| 1366 | int onenand_scan(struct mtd_info *mtd, int maxchips) | ||
| 1367 | { | ||
| 1368 | struct onenand_chip *this = mtd->priv; | ||
| 1369 | |||
| 1370 | if (!this->read_word) | ||
| 1371 | this->read_word = onenand_readw; | ||
| 1372 | if (!this->write_word) | ||
| 1373 | this->write_word = onenand_writew; | ||
| 1374 | |||
| 1375 | if (!this->command) | ||
| 1376 | this->command = onenand_command; | ||
| 1377 | if (!this->wait) | ||
| 1378 | this->wait = onenand_wait; | ||
| 1379 | |||
| 1380 | if (!this->read_bufferram) | ||
| 1381 | this->read_bufferram = onenand_read_bufferram; | ||
| 1382 | if (!this->write_bufferram) | ||
| 1383 | this->write_bufferram = onenand_write_bufferram; | ||
| 1384 | |||
| 1385 | if (onenand_probe(mtd)) | ||
| 1386 | return -ENXIO; | ||
| 1387 | |||
| 1388 | this->state = FL_READY; | ||
| 1389 | init_waitqueue_head(&this->wq); | ||
| 1390 | spin_lock_init(&this->chip_lock); | ||
| 1391 | |||
| 1392 | switch (mtd->oobsize) { | ||
| 1393 | case 64: | ||
| 1394 | this->autooob = &onenand_oob_64; | ||
| 1395 | break; | ||
| 1396 | |||
| 1397 | case 32: | ||
| 1398 | this->autooob = &onenand_oob_32; | ||
| 1399 | break; | ||
| 1400 | |||
| 1401 | default: | ||
| 1402 | printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", | ||
| 1403 | mtd->oobsize); | ||
| 1404 | /* To prevent kernel oops */ | ||
| 1405 | this->autooob = &onenand_oob_32; | ||
| 1406 | break; | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); | ||
| 1410 | |||
| 1411 | /* Fill in remaining MTD driver data */ | ||
| 1412 | mtd->type = MTD_NANDFLASH; | ||
| 1413 | mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; | ||
| 1414 | mtd->ecctype = MTD_ECC_SW; | ||
| 1415 | mtd->erase = onenand_erase; | ||
| 1416 | mtd->point = NULL; | ||
| 1417 | mtd->unpoint = NULL; | ||
| 1418 | mtd->read = onenand_read; | ||
| 1419 | mtd->write = onenand_write; | ||
| 1420 | mtd->read_ecc = onenand_read_ecc; | ||
| 1421 | mtd->write_ecc = onenand_write_ecc; | ||
| 1422 | mtd->read_oob = onenand_read_oob; | ||
| 1423 | mtd->write_oob = onenand_write_oob; | ||
| 1424 | mtd->readv = NULL; | ||
| 1425 | mtd->readv_ecc = NULL; | ||
| 1426 | mtd->writev = onenand_writev; | ||
| 1427 | mtd->writev_ecc = onenand_writev_ecc; | ||
| 1428 | mtd->sync = onenand_sync; | ||
| 1429 | mtd->lock = NULL; | ||
| 1430 | mtd->unlock = onenand_unlock; | ||
| 1431 | mtd->suspend = NULL; | ||
| 1432 | mtd->resume = NULL; | ||
| 1433 | mtd->block_isbad = onenand_block_isbad; | ||
| 1434 | mtd->block_markbad = onenand_block_markbad; | ||
| 1435 | mtd->owner = THIS_MODULE; | ||
| 1436 | |||
| 1437 | /* Unlock whole block */ | ||
| 1438 | mtd->unlock(mtd, 0x0, this->chipsize); | ||
| 1439 | |||
| 1440 | return 0; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | /** | ||
| 1444 | * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device | ||
| 1445 | * @param mtd MTD device structure | ||
| 1446 | */ | ||
| 1447 | void onenand_release(struct mtd_info *mtd) | ||
| 1448 | { | ||
| 1449 | #ifdef CONFIG_MTD_PARTITIONS | ||
| 1450 | /* Deregister partitions */ | ||
| 1451 | del_mtd_partitions (mtd); | ||
| 1452 | #endif | ||
| 1453 | /* Deregister the device */ | ||
| 1454 | del_mtd_device (mtd); | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | EXPORT_SYMBOL_GPL(onenand_scan); | ||
| 1458 | EXPORT_SYMBOL_GPL(onenand_release); | ||
| 1459 | |||
| 1460 | MODULE_LICENSE("GPL"); | ||
| 1461 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | ||
| 1462 | MODULE_DESCRIPTION("Generic OneNAND flash driver code"); | ||
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h new file mode 100644 index 000000000000..b9a64117d646 --- /dev/null +++ b/include/linux/mtd/onenand.h | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | /* | ||
| 2 | * linux/include/linux/mtd/onenand.h | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Samsung Electronics | ||
| 5 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify | ||
| 8 | * it under the terms of the GNU General Public License version 2 as | ||
| 9 | * published by the Free Software Foundation. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __LINUX_MTD_ONENAND_H | ||
| 13 | #define __LINUX_MTD_ONENAND_H | ||
| 14 | |||
| 15 | #include <linux/spinlock.h> | ||
| 16 | #include <linux/mtd/onenand_regs.h> | ||
| 17 | |||
| 18 | #define MAX_BUFFERRAM 2 | ||
| 19 | |||
| 20 | /* Scan and identify a OneNAND device */ | ||
| 21 | extern int onenand_scan(struct mtd_info *mtd, int max_chips); | ||
| 22 | /* Free resources held by the OneNAND device */ | ||
| 23 | extern void onenand_release(struct mtd_info *mtd); | ||
| 24 | |||
| 25 | /** | ||
| 26 | * onenand_state_t - chip states | ||
| 27 | * Enumeration for OneNAND flash chip state | ||
| 28 | */ | ||
| 29 | typedef enum { | ||
| 30 | FL_READY, | ||
| 31 | FL_READING, | ||
| 32 | FL_WRITING, | ||
| 33 | FL_ERASING, | ||
| 34 | FL_SYNCING, | ||
| 35 | FL_UNLOCKING, | ||
| 36 | FL_LOCKING, | ||
| 37 | } onenand_state_t; | ||
| 38 | |||
| 39 | /** | ||
| 40 | * struct onenand_bufferram - OneNAND BufferRAM Data | ||
| 41 | * @param block block address in BufferRAM | ||
| 42 | * @param page page address in BufferRAM | ||
| 43 | * @param valid valid flag | ||
| 44 | */ | ||
| 45 | struct onenand_bufferram { | ||
| 46 | int block; | ||
| 47 | int page; | ||
| 48 | int valid; | ||
| 49 | }; | ||
| 50 | |||
| 51 | /** | ||
| 52 | * struct onenand_chip - OneNAND Private Flash Chip Data | ||
| 53 | * @param base [BOARDSPECIFIC] address to access OneNAND | ||
| 54 | * @param chipsize [INTERN] the size of one chip for multichip arrays | ||
| 55 | * @param device_id [INTERN] device ID | ||
| 56 | * @param verstion_id [INTERN] version ID | ||
| 57 | * @param options [BOARDSPECIFIC] various chip options. They can partly be set to inform onenand_scan about | ||
| 58 | * @param erase_shift [INTERN] number of address bits in a block | ||
| 59 | * @param page_shift [INTERN] number of address bits in a page | ||
| 60 | * @param ppb_shift [INTERN] number of address bits in a pages per block | ||
| 61 | * @param page_mask [INTERN] a page per block mask | ||
| 62 | * @param bufferam_index [INTERN] BufferRAM index | ||
| 63 | * @param bufferam [INTERN] BufferRAM info | ||
| 64 | * @param readw [REPLACEABLE] hardware specific function for read short | ||
| 65 | * @param writew [REPLACEABLE] hardware specific function for write short | ||
| 66 | * @param command [REPLACEABLE] hardware specific function for writing commands to the chip | ||
| 67 | * @param wait [REPLACEABLE] hardware specific function for wait on ready | ||
| 68 | * @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area | ||
| 69 | * @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area | ||
| 70 | * @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip | ||
| 71 | * @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress | ||
| 72 | * @param state [INTERN] the current state of the OneNAND device | ||
| 73 | * @param autooob [REPLACEABLE] the default (auto)placement scheme | ||
| 74 | * @param priv [OPTIONAL] pointer to private chip date | ||
| 75 | */ | ||
| 76 | struct onenand_chip { | ||
| 77 | void __iomem *base; | ||
| 78 | unsigned int chipsize; | ||
| 79 | unsigned int device_id; | ||
| 80 | unsigned int options; | ||
| 81 | |||
| 82 | unsigned int erase_shift; | ||
| 83 | unsigned int page_shift; | ||
| 84 | unsigned int ppb_shift; /* Pages per block shift */ | ||
| 85 | unsigned int page_mask; | ||
| 86 | |||
| 87 | unsigned int bufferram_index; | ||
| 88 | struct onenand_bufferram bufferram[MAX_BUFFERRAM]; | ||
| 89 | |||
| 90 | int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len); | ||
| 91 | int (*wait)(struct mtd_info *mtd, int state); | ||
| 92 | int (*read_bufferram)(struct mtd_info *mtd, int area, | ||
| 93 | unsigned char *buffer, int offset, size_t count); | ||
| 94 | int (*write_bufferram)(struct mtd_info *mtd, int area, | ||
| 95 | const unsigned char *buffer, int offset, size_t count); | ||
| 96 | unsigned short (*read_word)(void __iomem *addr); | ||
| 97 | void (*write_word)(unsigned short value, void __iomem *addr); | ||
| 98 | |||
| 99 | spinlock_t chip_lock; | ||
| 100 | wait_queue_head_t wq; | ||
| 101 | onenand_state_t state; | ||
| 102 | |||
| 103 | struct nand_oobinfo *autooob; | ||
| 104 | |||
| 105 | void *priv; | ||
| 106 | }; | ||
| 107 | |||
| 108 | #define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index) | ||
| 109 | #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) | ||
| 110 | #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) | ||
| 111 | |||
| 112 | /* | ||
| 113 | * Options bits | ||
| 114 | */ | ||
| 115 | #define ONENAND_CONT_LOCK (0x0001) | ||
| 116 | |||
| 117 | |||
| 118 | /* | ||
| 119 | * OneNAND Flash Manufacturer ID Codes | ||
| 120 | */ | ||
| 121 | #define ONENAND_MFR_SAMSUNG 0xec | ||
| 122 | #define ONENAND_MFR_UNKNOWN 0x00 | ||
| 123 | |||
| 124 | /** | ||
| 125 | * struct nand_manufacturers - NAND Flash Manufacturer ID Structure | ||
| 126 | * @param name: Manufacturer name | ||
| 127 | * @param id: manufacturer ID code of device. | ||
| 128 | */ | ||
| 129 | struct onenand_manufacturers { | ||
| 130 | int id; | ||
| 131 | char *name; | ||
| 132 | }; | ||
| 133 | |||
| 134 | #endif /* __LINUX_MTD_ONENAND_H */ | ||
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h new file mode 100644 index 000000000000..4a2daad7d738 --- /dev/null +++ b/include/linux/mtd/onenand_regs.h | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | /* | ||
| 2 | * linux/include/linux/mtd/onenand_regs.h | ||
| 3 | * | ||
| 4 | * OneNAND Register header file | ||
| 5 | * | ||
| 6 | * Copyright (C) 2005 Samsung Electronics | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #ifndef __ONENAND_REG_H | ||
| 14 | #define __ONENAND_REG_H | ||
| 15 | |||
| 16 | /* Memory Address Map Translation (Word order) */ | ||
| 17 | #define ONENAND_MEMORY_MAP(x) ((x) << 1) | ||
| 18 | |||
| 19 | /* | ||
| 20 | * External BufferRAM area | ||
| 21 | */ | ||
| 22 | #define ONENAND_BOOTRAM ONENAND_MEMORY_MAP(0x0000) | ||
| 23 | #define ONENAND_DATARAM ONENAND_MEMORY_MAP(0x0200) | ||
| 24 | #define ONENAND_SPARERAM ONENAND_MEMORY_MAP(0x8010) | ||
| 25 | |||
| 26 | /* | ||
| 27 | * OneNAND Registers | ||
| 28 | */ | ||
| 29 | #define ONENAND_REG_MANUFACTURER_ID ONENAND_MEMORY_MAP(0xF000) | ||
| 30 | #define ONENAND_REG_DEVICE_ID ONENAND_MEMORY_MAP(0xF001) | ||
| 31 | #define ONENAND_REG_VERSION_ID ONENAND_MEMORY_MAP(0xF002) | ||
| 32 | #define ONENAND_REG_DATA_BUFFER_SIZE ONENAND_MEMORY_MAP(0xF003) | ||
| 33 | #define ONENAND_REG_BOOT_BUFFER_SIZE ONENAND_MEMORY_MAP(0xF004) | ||
| 34 | #define ONENAND_REG_NUM_BUFFERS ONENAND_MEMORY_MAP(0xF005) | ||
| 35 | #define ONENAND_REG_TECHNOLOGY ONENAND_MEMORY_MAP(0xF006) | ||
| 36 | |||
| 37 | #define ONENAND_REG_START_ADDRESS1 ONENAND_MEMORY_MAP(0xF100) | ||
| 38 | #define ONENAND_REG_START_ADDRESS2 ONENAND_MEMORY_MAP(0xF101) | ||
| 39 | #define ONENAND_REG_START_ADDRESS3 ONENAND_MEMORY_MAP(0xF102) | ||
| 40 | #define ONENAND_REG_START_ADDRESS4 ONENAND_MEMORY_MAP(0xF103) | ||
| 41 | #define ONENAND_REG_START_ADDRESS5 ONENAND_MEMORY_MAP(0xF104) | ||
| 42 | #define ONENAND_REG_START_ADDRESS6 ONENAND_MEMORY_MAP(0xF105) | ||
| 43 | #define ONENAND_REG_START_ADDRESS7 ONENAND_MEMORY_MAP(0xF106) | ||
| 44 | #define ONENAND_REG_START_ADDRESS8 ONENAND_MEMORY_MAP(0xF107) | ||
| 45 | |||
| 46 | #define ONENAND_REG_START_BUFFER ONENAND_MEMORY_MAP(0xF200) | ||
| 47 | #define ONENAND_REG_COMMAND ONENAND_MEMORY_MAP(0xF220) | ||
| 48 | #define ONENAND_REG_SYS_CFG1 ONENAND_MEMORY_MAP(0xF221) | ||
| 49 | #define ONENAND_REG_SYS_CFG2 ONENAND_MEMORY_MAP(0xF222) | ||
| 50 | #define ONENAND_REG_CTRL_STATUS ONENAND_MEMORY_MAP(0xF240) | ||
| 51 | #define ONENAND_REG_INTERRUPT ONENAND_MEMORY_MAP(0xF241) | ||
| 52 | #define ONENAND_REG_START_BLOCK_ADDRESS ONENAND_MEMORY_MAP(0xF24C) | ||
| 53 | #define ONENAND_REG_END_BLOCK_ADDRESS ONENAND_MEMORY_MAP(0xF24D) | ||
| 54 | #define ONENAND_REG_WP_STATUS ONENAND_MEMORY_MAP(0xF24E) | ||
| 55 | |||
| 56 | #define ONENAND_REG_ECC_STATUS ONENAND_MEMORY_MAP(0xFF00) | ||
| 57 | #define ONENAND_REG_ECC_M0 ONENAND_MEMORY_MAP(0xFF01) | ||
| 58 | #define ONENAND_REG_ECC_S0 ONENAND_MEMORY_MAP(0xFF02) | ||
| 59 | #define ONENAND_REG_ECC_M1 ONENAND_MEMORY_MAP(0xFF03) | ||
| 60 | #define ONENAND_REG_ECC_S1 ONENAND_MEMORY_MAP(0xFF04) | ||
| 61 | #define ONENAND_REG_ECC_M2 ONENAND_MEMORY_MAP(0xFF05) | ||
| 62 | #define ONENAND_REG_ECC_S2 ONENAND_MEMORY_MAP(0xFF06) | ||
| 63 | #define ONENAND_REG_ECC_M3 ONENAND_MEMORY_MAP(0xFF07) | ||
| 64 | #define ONENAND_REG_ECC_S3 ONENAND_MEMORY_MAP(0xFF08) | ||
| 65 | |||
| 66 | /* | ||
| 67 | * Device ID Register F001h (R) | ||
| 68 | */ | ||
| 69 | #define ONENAND_DEVICE_DENSITY_SHIFT (4) | ||
| 70 | #define ONENAND_DEVICE_IS_DDP (1 << 3) | ||
| 71 | #define ONENAND_DEVICE_IS_DEMUX (1 << 2) | ||
| 72 | #define ONENAND_DEVICE_VCC_MASK (0x3) | ||
| 73 | |||
| 74 | #define ONENAND_DEVICE_DENSITY_512Mb (0x002) | ||
| 75 | |||
| 76 | /* | ||
| 77 | * Version ID Register F002h (R) | ||
| 78 | */ | ||
| 79 | #define ONENAND_VERSION_PROCESS_SHIFT (8) | ||
| 80 | |||
| 81 | /* | ||
| 82 | * Start Address 1 F100h (R/W) | ||
| 83 | */ | ||
| 84 | #define ONENAND_DDP_SHIFT (15) | ||
| 85 | |||
| 86 | /* | ||
| 87 | * Start Address 8 F107h (R/W) | ||
| 88 | */ | ||
| 89 | #define ONENAND_FPA_MASK (0x3f) | ||
| 90 | #define ONENAND_FPA_SHIFT (2) | ||
| 91 | #define ONENAND_FSA_MASK (0x03) | ||
| 92 | |||
| 93 | /* | ||
| 94 | * Start Buffer Register F200h (R/W) | ||
| 95 | */ | ||
| 96 | #define ONENAND_BSA_MASK (0x03) | ||
| 97 | #define ONENAND_BSA_SHIFT (8) | ||
| 98 | #define ONENAND_BSA_BOOTRAM (0 << 2) | ||
| 99 | #define ONENAND_BSA_DATARAM0 (2 << 2) | ||
| 100 | #define ONENAND_BSA_DATARAM1 (3 << 2) | ||
| 101 | #define ONENAND_BSC_MASK (0x03) | ||
| 102 | |||
| 103 | /* | ||
| 104 | * Command Register F220h (R/W) | ||
| 105 | */ | ||
| 106 | #define ONENAND_CMD_READ (0x00) | ||
| 107 | #define ONENAND_CMD_READOOB (0x13) | ||
| 108 | #define ONENAND_CMD_PROG (0x80) | ||
| 109 | #define ONENAND_CMD_PROGOOB (0x1A) | ||
| 110 | #define ONENAND_CMD_UNLOCK (0x23) | ||
| 111 | #define ONENAND_CMD_LOCK (0x2A) | ||
| 112 | #define ONENAND_CMD_LOCK_TIGHT (0x2C) | ||
| 113 | #define ONENAND_CMD_ERASE (0x94) | ||
| 114 | #define ONENAND_CMD_RESET (0xF0) | ||
| 115 | #define ONENAND_CMD_READID (0x90) | ||
| 116 | |||
| 117 | /* NOTE: Those are not *REAL* commands */ | ||
| 118 | #define ONENAND_CMD_BUFFERRAM (0x1978) | ||
| 119 | |||
| 120 | /* | ||
| 121 | * System Configuration 1 Register F221h (R, R/W) | ||
| 122 | */ | ||
| 123 | #define ONENAND_SYS_CFG1_SYNC_READ (1 << 15) | ||
| 124 | #define ONENAND_SYS_CFG1_BRL (1 << 12) | ||
| 125 | #define ONENAND_SYS_CFG1_BL (1 << 9) | ||
| 126 | #define ONENAND_SYS_CFG1_NO_ECC (1 << 8) | ||
| 127 | #define ONENAND_SYS_CFG1_RDY (1 << 7) | ||
| 128 | #define ONENAND_SYS_CFG1_INT (1 << 6) | ||
| 129 | #define ONENAND_SYS_CFG1_IOBE (1 << 5) | ||
| 130 | #define ONENAND_SYS_CFG1_RDY_CONF (1 << 4) | ||
| 131 | |||
| 132 | /* | ||
| 133 | * Controller Status Register F240h (R) | ||
| 134 | */ | ||
| 135 | #define ONENAND_CTRL_ONGO (1 << 15) | ||
| 136 | #define ONENAND_CTRL_LOCK (1 << 14) | ||
| 137 | #define ONENAND_CTRL_LOAD (1 << 13) | ||
| 138 | #define ONENAND_CTRL_PROGRAM (1 << 12) | ||
| 139 | #define ONENAND_CTRL_ERASE (1 << 11) | ||
| 140 | #define ONENAND_CTRL_ERROR (1 << 10) | ||
| 141 | #define ONENAND_CTRL_RSTB (1 << 7) | ||
| 142 | |||
| 143 | /* | ||
| 144 | * Interrupt Status Register F241h (R) | ||
| 145 | */ | ||
| 146 | #define ONENAND_INT_MASTER (1 << 15) | ||
| 147 | #define ONENAND_INT_READ (1 << 7) | ||
| 148 | #define ONENAND_INT_WRITE (1 << 6) | ||
| 149 | #define ONENAND_INT_ERASE (1 << 5) | ||
| 150 | #define ONENAND_INT_RESET (1 << 4) | ||
| 151 | #define ONENAND_INT_CLEAR (0 << 0) | ||
| 152 | |||
| 153 | /* | ||
| 154 | * NAND Flash Write Protection Status Register F24Eh (R) | ||
| 155 | */ | ||
| 156 | #define ONENAND_WP_US (1 << 2) | ||
| 157 | #define ONENAND_WP_LS (1 << 1) | ||
| 158 | #define ONENAND_WP_LTS (1 << 0) | ||
| 159 | |||
| 160 | /* | ||
| 161 | * ECC Status Reigser FF00h (R) | ||
| 162 | */ | ||
| 163 | #define ONENAND_ECC_1BIT (1 << 0) | ||
| 164 | #define ONENAND_ECC_2BIT (1 << 1) | ||
| 165 | #define ONENAND_ECC_2BIT_ALL (0xAAAA) | ||
| 166 | |||
| 167 | #endif /* __ONENAND_REG_H */ | ||
