aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLeo (Hao) Chen <leochen@broadcom.com>2009-10-09 22:13:08 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2009-10-19 21:07:23 -0400
commit266dead21675aeb89407b1213788cd924353d5e1 (patch)
tree349eb9b220169a94dab54014da4f97051134db0c /drivers
parent4b56ffcacee937a85bf39e14872dd141e23ee85f (diff)
mtd: add bcmring nand driver
Signed-off-by: Leo Hao Chen <leochen@broadcom.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/Kconfig16
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/bcm_umi_bch.c213
-rw-r--r--drivers/mtd/nand/bcm_umi_nand.c581
-rw-r--r--drivers/mtd/nand/nand_bcm_umi.c149
-rw-r--r--drivers/mtd/nand/nand_bcm_umi.h358
6 files changed, 1318 insertions, 0 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2fda0b615246..34598e9c1a40 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -203,6 +203,22 @@ config MTD_NAND_S3C2410_CLKSTOP
203 when the is NAND chip selected or released, but will save 203 when the is NAND chip selected or released, but will save
204 approximately 5mA of power when there is nothing happening. 204 approximately 5mA of power when there is nothing happening.
205 205
206config MTD_NAND_BCM_UMI
207 tristate "NAND Flash support for BCM Reference Boards"
208 depends on ARCH_BCMRING && MTD_NAND
209 help
210 This enables the NAND flash controller on the BCM UMI block.
211
212 No board specfic support is done by this driver, each board
213 must advertise a platform_device for the driver to attach.
214
215config MTD_NAND_BCM_UMI_HWCS
216 bool "BCM UMI NAND Hardware CS"
217 depends on MTD_NAND_BCM_UMI
218 help
219 Enable the use of the BCM UMI block's internal CS using NAND.
220 This should only be used if you know the external NAND CS can toggle.
221
206config MTD_NAND_DISKONCHIP 222config MTD_NAND_DISKONCHIP
207 tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" 223 tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
208 depends on EXPERIMENTAL 224 depends on EXPERIMENTAL
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 6950d3dabf10..460a1f39a8d1 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
42obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o 42obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
43obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o 43obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o
44obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o 44obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o
45obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o
45 46
46nand-objs := nand_base.o nand_bbt.o 47nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c
new file mode 100644
index 000000000000..a930666d0687
--- /dev/null
+++ b/drivers/mtd/nand/bcm_umi_bch.c
@@ -0,0 +1,213 @@
1/*****************************************************************************
2* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
3*
4* Unless you and Broadcom execute a separate written software license
5* agreement governing use of this software, this software is licensed to you
6* under the terms of the GNU General Public License version 2, available at
7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8*
9* Notwithstanding the above, under no circumstances may you combine this
10* software in any way with any other Broadcom software provided under a
11* license other than the GPL, without Broadcom's express prior written
12* consent.
13*****************************************************************************/
14
15/* ---- Include Files ---------------------------------------------------- */
16#include "nand_bcm_umi.h"
17
18/* ---- External Variable Declarations ----------------------------------- */
19/* ---- External Function Prototypes ------------------------------------- */
20/* ---- Public Variables ------------------------------------------------- */
21/* ---- Private Constants and Types -------------------------------------- */
22
23/* ---- Private Function Prototypes -------------------------------------- */
24static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
25 struct nand_chip *chip, uint8_t *buf, int page);
26static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
27 struct nand_chip *chip, const uint8_t *buf);
28
29/* ---- Private Variables ------------------------------------------------ */
30
31/*
32** nand_hw_eccoob
33** New oob placement block for use with hardware ecc generation.
34*/
35static struct nand_ecclayout nand_hw_eccoob_512 = {
36 /* Reserve 5 for BI indicator */
37 .oobfree = {
38#if (NAND_ECC_NUM_BYTES > 3)
39 {.offset = 0, .length = 2}
40#else
41 {.offset = 0, .length = 5},
42 {.offset = 6, .length = 7}
43#endif
44 }
45};
46
47/*
48** We treat the OOB for a 2K page as if it were 4 512 byte oobs,
49** except the BI is at byte 0.
50*/
51static struct nand_ecclayout nand_hw_eccoob_2048 = {
52 /* Reserve 0 as BI indicator */
53 .oobfree = {
54#if (NAND_ECC_NUM_BYTES > 10)
55 {.offset = 1, .length = 2},
56#elif (NAND_ECC_NUM_BYTES > 7)
57 {.offset = 1, .length = 5},
58 {.offset = 16, .length = 6},
59 {.offset = 32, .length = 6},
60 {.offset = 48, .length = 6}
61#else
62 {.offset = 1, .length = 8},
63 {.offset = 16, .length = 9},
64 {.offset = 32, .length = 9},
65 {.offset = 48, .length = 9}
66#endif
67 }
68};
69
70/* We treat the OOB for a 4K page as if it were 8 512 byte oobs,
71 * except the BI is at byte 0. */
72static struct nand_ecclayout nand_hw_eccoob_4096 = {
73 /* Reserve 0 as BI indicator */
74 .oobfree = {
75#if (NAND_ECC_NUM_BYTES > 10)
76 {.offset = 1, .length = 2},
77 {.offset = 16, .length = 3},
78 {.offset = 32, .length = 3},
79 {.offset = 48, .length = 3},
80 {.offset = 64, .length = 3},
81 {.offset = 80, .length = 3},
82 {.offset = 96, .length = 3},
83 {.offset = 112, .length = 3}
84#else
85 {.offset = 1, .length = 5},
86 {.offset = 16, .length = 6},
87 {.offset = 32, .length = 6},
88 {.offset = 48, .length = 6},
89 {.offset = 64, .length = 6},
90 {.offset = 80, .length = 6},
91 {.offset = 96, .length = 6},
92 {.offset = 112, .length = 6}
93#endif
94 }
95};
96
97/* ---- Private Functions ------------------------------------------------ */
98/* ==== Public Functions ================================================= */
99
100/****************************************************************************
101*
102* bcm_umi_bch_read_page_hwecc - hardware ecc based page read function
103* @mtd: mtd info structure
104* @chip: nand chip info structure
105* @buf: buffer to store read data
106*
107***************************************************************************/
108static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
109 struct nand_chip *chip, uint8_t * buf,
110 int page)
111{
112 int sectorIdx = 0;
113 int eccsize = chip->ecc.size;
114 int eccsteps = chip->ecc.steps;
115 uint8_t *datap = buf;
116 uint8_t eccCalc[NAND_ECC_NUM_BYTES];
117 int sectorOobSize = mtd->oobsize / eccsteps;
118 int stat;
119
120 for (sectorIdx = 0; sectorIdx < eccsteps;
121 sectorIdx++, datap += eccsize) {
122 if (sectorIdx > 0) {
123 /* Seek to page location within sector */
124 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
125 -1);
126 }
127
128 /* Enable hardware ECC before reading the buf */
129 nand_bcm_umi_bch_enable_read_hwecc();
130
131 /* Read in data */
132 bcm_umi_nand_read_buf(mtd, datap, eccsize);
133
134 /* Pause hardware ECC after reading the buf */
135 nand_bcm_umi_bch_pause_read_ecc_calc();
136
137 /* Read the OOB ECC */
138 chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
139 mtd->writesize + sectorIdx * sectorOobSize, -1);
140 nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
141 NAND_ECC_NUM_BYTES,
142 chip->oob_poi +
143 sectorIdx * sectorOobSize);
144
145 /* Correct any ECC detected errors */
146 stat =
147 nand_bcm_umi_bch_correct_page(datap, eccCalc,
148 NAND_ECC_NUM_BYTES);
149
150 /* Update Stats */
151 if (stat < 0) {
152#if defined(NAND_BCM_UMI_DEBUG)
153 printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
154 __func__, sectorIdx);
155 printk(KERN_WARNING
156 "%s data %02x %02x %02x %02x "
157 "%02x %02x %02x %02x\n",
158 __func__, datap[0], datap[1], datap[2], datap[3],
159 datap[4], datap[5], datap[6], datap[7]);
160 printk(KERN_WARNING
161 "%s ecc %02x %02x %02x %02x "
162 "%02x %02x %02x %02x %02x %02x "
163 "%02x %02x %02x\n",
164 __func__, eccCalc[0], eccCalc[1], eccCalc[2],
165 eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
166 eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
167 eccCalc[11], eccCalc[12]);
168 BUG();
169#endif
170 mtd->ecc_stats.failed++;
171 } else {
172#if defined(NAND_BCM_UMI_DEBUG)
173 if (stat > 0) {
174 printk(KERN_INFO
175 "%s %d correctable_errors detected\n",
176 __func__, stat);
177 }
178#endif
179 mtd->ecc_stats.corrected += stat;
180 }
181 }
182 return 0;
183}
184
185/****************************************************************************
186*
187* bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
188* @mtd: mtd info structure
189* @chip: nand chip info structure
190* @buf: data buffer
191*
192***************************************************************************/
193static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
194 struct nand_chip *chip, const uint8_t *buf)
195{
196 int sectorIdx = 0;
197 int eccsize = chip->ecc.size;
198 int eccsteps = chip->ecc.steps;
199 const uint8_t *datap = buf;
200 uint8_t *oobp = chip->oob_poi;
201 int sectorOobSize = mtd->oobsize / eccsteps;
202
203 for (sectorIdx = 0; sectorIdx < eccsteps;
204 sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
205 /* Enable hardware ECC before writing the buf */
206 nand_bcm_umi_bch_enable_write_hwecc();
207 bcm_umi_nand_write_buf(mtd, datap, eccsize);
208 nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
209 NAND_ECC_NUM_BYTES);
210 }
211
212 bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
213}
diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c
new file mode 100644
index 000000000000..087bcd745bb7
--- /dev/null
+++ b/drivers/mtd/nand/bcm_umi_nand.c
@@ -0,0 +1,581 @@
1/*****************************************************************************
2* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
3*
4* Unless you and Broadcom execute a separate written software license
5* agreement governing use of this software, this software is licensed to you
6* under the terms of the GNU General Public License version 2, available at
7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8*
9* Notwithstanding the above, under no circumstances may you combine this
10* software in any way with any other Broadcom software provided under a
11* license other than the GPL, without Broadcom's express prior written
12* consent.
13*****************************************************************************/
14
15/* ---- Include Files ---------------------------------------------------- */
16#include <linux/version.h>
17#include <linux/module.h>
18#include <linux/types.h>
19#include <linux/init.h>
20#include <linux/kernel.h>
21#include <linux/string.h>
22#include <linux/ioport.h>
23#include <linux/device.h>
24#include <linux/delay.h>
25#include <linux/err.h>
26#include <linux/io.h>
27#include <linux/platform_device.h>
28#include <linux/mtd/mtd.h>
29#include <linux/mtd/nand.h>
30#include <linux/mtd/nand_ecc.h>
31#include <linux/mtd/partitions.h>
32
33#include <asm/mach-types.h>
34#include <asm/system.h>
35
36#include <mach/reg_nand.h>
37#include <mach/reg_umi.h>
38
39#include "nand_bcm_umi.h"
40
41#include <mach/memory_settings.h>
42
43#define USE_DMA 1
44#include <mach/dma.h>
45#include <linux/dma-mapping.h>
46#include <linux/completion.h>
47
48/* ---- External Variable Declarations ----------------------------------- */
49/* ---- External Function Prototypes ------------------------------------- */
50/* ---- Public Variables ------------------------------------------------- */
51/* ---- Private Constants and Types -------------------------------------- */
52static const __devinitconst char gBanner[] = KERN_INFO \
53 "BCM UMI MTD NAND Driver: 1.00\n";
54
55#ifdef CONFIG_MTD_PARTITIONS
56const char *part_probes[] = { "cmdlinepart", NULL };
57#endif
58
59#if NAND_ECC_BCH
60static uint8_t scan_ff_pattern[] = { 0xff };
61
62static struct nand_bbt_descr largepage_bbt = {
63 .options = 0,
64 .offs = 0,
65 .len = 1,
66 .pattern = scan_ff_pattern
67};
68#endif
69
70/*
71** Preallocate a buffer to avoid having to do this every dma operation.
72** This is the size of the preallocated coherent DMA buffer.
73*/
74#if USE_DMA
75#define DMA_MIN_BUFLEN 512
76#define DMA_MAX_BUFLEN PAGE_SIZE
77#define USE_DIRECT_IO(len) (((len) < DMA_MIN_BUFLEN) || \
78 ((len) > DMA_MAX_BUFLEN))
79
80/*
81 * The current NAND data space goes from 0x80001900 to 0x80001FFF,
82 * which is only 0x700 = 1792 bytes long. This is too small for 2K, 4K page
83 * size NAND flash. Need to break the DMA down to multiple 1Ks.
84 *
85 * Need to make sure REG_NAND_DATA_PADDR + DMA_MAX_LEN < 0x80002000
86 */
87#define DMA_MAX_LEN 1024
88
89#else /* !USE_DMA */
90#define DMA_MIN_BUFLEN 0
91#define DMA_MAX_BUFLEN 0
92#define USE_DIRECT_IO(len) 1
93#endif
94/* ---- Private Function Prototypes -------------------------------------- */
95static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len);
96static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf,
97 int len);
98
99/* ---- Private Variables ------------------------------------------------ */
100static struct mtd_info *board_mtd;
101static void __iomem *bcm_umi_io_base;
102static void *virtPtr;
103static dma_addr_t physPtr;
104static struct completion nand_comp;
105
106/* ---- Private Functions ------------------------------------------------ */
107#if NAND_ECC_BCH
108#include "bcm_umi_bch.c"
109#else
110#include "bcm_umi_hamming.c"
111#endif
112
113#if USE_DMA
114
115/* Handler called when the DMA finishes. */
116static void nand_dma_handler(DMA_Device_t dev, int reason, void *userData)
117{
118 complete(&nand_comp);
119}
120
121static int nand_dma_init(void)
122{
123 int rc;
124
125 rc = dma_set_device_handler(DMA_DEVICE_NAND_MEM_TO_MEM,
126 nand_dma_handler, NULL);
127 if (rc != 0) {
128 printk(KERN_ERR "dma_set_device_handler failed: %d\n", rc);
129 return rc;
130 }
131
132 virtPtr =
133 dma_alloc_coherent(NULL, DMA_MAX_BUFLEN, &physPtr, GFP_KERNEL);
134 if (virtPtr == NULL) {
135 printk(KERN_ERR "NAND - Failed to allocate memory for DMA buffer\n");
136 return -ENOMEM;
137 }
138
139 return 0;
140}
141
142static void nand_dma_term(void)
143{
144 if (virtPtr != NULL)
145 dma_free_coherent(NULL, DMA_MAX_BUFLEN, virtPtr, physPtr);
146}
147
148static void nand_dma_read(void *buf, int len)
149{
150 int offset = 0;
151 int tmp_len = 0;
152 int len_left = len;
153 DMA_Handle_t hndl;
154
155 if (virtPtr == NULL)
156 panic("nand_dma_read: virtPtr == NULL\n");
157
158 if ((void *)physPtr == NULL)
159 panic("nand_dma_read: physPtr == NULL\n");
160
161 hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM);
162 if (hndl < 0) {
163 printk(KERN_ERR
164 "nand_dma_read: unable to allocate dma channel: %d\n",
165 (int)hndl);
166 panic("\n");
167 }
168
169 while (len_left > 0) {
170 if (len_left > DMA_MAX_LEN) {
171 tmp_len = DMA_MAX_LEN;
172 len_left -= DMA_MAX_LEN;
173 } else {
174 tmp_len = len_left;
175 len_left = 0;
176 }
177
178 init_completion(&nand_comp);
179 dma_transfer_mem_to_mem(hndl, REG_NAND_DATA_PADDR,
180 physPtr + offset, tmp_len);
181 wait_for_completion(&nand_comp);
182
183 offset += tmp_len;
184 }
185
186 dma_free_channel(hndl);
187
188 if (buf != NULL)
189 memcpy(buf, virtPtr, len);
190}
191
192static void nand_dma_write(const void *buf, int len)
193{
194 int offset = 0;
195 int tmp_len = 0;
196 int len_left = len;
197 DMA_Handle_t hndl;
198
199 if (buf == NULL)
200 panic("nand_dma_write: buf == NULL\n");
201
202 if (virtPtr == NULL)
203 panic("nand_dma_write: virtPtr == NULL\n");
204
205 if ((void *)physPtr == NULL)
206 panic("nand_dma_write: physPtr == NULL\n");
207
208 memcpy(virtPtr, buf, len);
209
210
211 hndl = dma_request_channel(DMA_DEVICE_NAND_MEM_TO_MEM);
212 if (hndl < 0) {
213 printk(KERN_ERR
214 "nand_dma_write: unable to allocate dma channel: %d\n",
215 (int)hndl);
216 panic("\n");
217 }
218
219 while (len_left > 0) {
220 if (len_left > DMA_MAX_LEN) {
221 tmp_len = DMA_MAX_LEN;
222 len_left -= DMA_MAX_LEN;
223 } else {
224 tmp_len = len_left;
225 len_left = 0;
226 }
227
228 init_completion(&nand_comp);
229 dma_transfer_mem_to_mem(hndl, physPtr + offset,
230 REG_NAND_DATA_PADDR, tmp_len);
231 wait_for_completion(&nand_comp);
232
233 offset += tmp_len;
234 }
235
236 dma_free_channel(hndl);
237}
238
239#endif
240
241static int nand_dev_ready(struct mtd_info *mtd)
242{
243 return nand_bcm_umi_dev_ready();
244}
245
246/****************************************************************************
247*
248* bcm_umi_nand_inithw
249*
250* This routine does the necessary hardware (board-specific)
251* initializations. This includes setting up the timings, etc.
252*
253***************************************************************************/
254int bcm_umi_nand_inithw(void)
255{
256 /* Configure nand timing parameters */
257 REG_UMI_NAND_TCR &= ~0x7ffff;
258 REG_UMI_NAND_TCR |= HW_CFG_NAND_TCR;
259
260#if !defined(CONFIG_MTD_NAND_BCM_UMI_HWCS)
261 /* enable software control of CS */
262 REG_UMI_NAND_TCR |= REG_UMI_NAND_TCR_CS_SWCTRL;
263#endif
264
265 /* keep NAND chip select asserted */
266 REG_UMI_NAND_RCSR |= REG_UMI_NAND_RCSR_CS_ASSERTED;
267
268 REG_UMI_NAND_TCR &= ~REG_UMI_NAND_TCR_WORD16;
269 /* enable writes to flash */
270 REG_UMI_MMD_ICR |= REG_UMI_MMD_ICR_FLASH_WP;
271
272 writel(NAND_CMD_RESET, bcm_umi_io_base + REG_NAND_CMD_OFFSET);
273 nand_bcm_umi_wait_till_ready();
274
275#if NAND_ECC_BCH
276 nand_bcm_umi_bch_config_ecc(NAND_ECC_NUM_BYTES);
277#endif
278
279 return 0;
280}
281
282/* Used to turn latch the proper register for access. */
283static void bcm_umi_nand_hwcontrol(struct mtd_info *mtd, int cmd,
284 unsigned int ctrl)
285{
286 /* send command to hardware */
287 struct nand_chip *chip = mtd->priv;
288 if (ctrl & NAND_CTRL_CHANGE) {
289 if (ctrl & NAND_CLE) {
290 chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_CMD_OFFSET;
291 goto CMD;
292 }
293 if (ctrl & NAND_ALE) {
294 chip->IO_ADDR_W =
295 bcm_umi_io_base + REG_NAND_ADDR_OFFSET;
296 goto CMD;
297 }
298 chip->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
299 }
300
301CMD:
302 /* Send command to chip directly */
303 if (cmd != NAND_CMD_NONE)
304 writeb(cmd, chip->IO_ADDR_W);
305}
306
307static void bcm_umi_nand_write_buf(struct mtd_info *mtd, const u_char * buf,
308 int len)
309{
310 if (USE_DIRECT_IO(len)) {
311 /* Do it the old way if the buffer is small or too large.
312 * Probably quicker than starting and checking dma. */
313 int i;
314 struct nand_chip *this = mtd->priv;
315
316 for (i = 0; i < len; i++)
317 writeb(buf[i], this->IO_ADDR_W);
318 }
319#if USE_DMA
320 else
321 nand_dma_write(buf, len);
322#endif
323}
324
325static void bcm_umi_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len)
326{
327 if (USE_DIRECT_IO(len)) {
328 int i;
329 struct nand_chip *this = mtd->priv;
330
331 for (i = 0; i < len; i++)
332 buf[i] = readb(this->IO_ADDR_R);
333 }
334#if USE_DMA
335 else
336 nand_dma_read(buf, len);
337#endif
338}
339
340static uint8_t readbackbuf[NAND_MAX_PAGESIZE];
341static int bcm_umi_nand_verify_buf(struct mtd_info *mtd, const u_char * buf,
342 int len)
343{
344 /*
345 * Try to readback page with ECC correction. This is necessary
346 * for MLC parts which may have permanently stuck bits.
347 */
348 struct nand_chip *chip = mtd->priv;
349 int ret = chip->ecc.read_page(mtd, chip, readbackbuf, 0);
350 if (ret < 0)
351 return -EFAULT;
352 else {
353 if (memcmp(readbackbuf, buf, len) == 0)
354 return 0;
355
356 return -EFAULT;
357 }
358 return 0;
359}
360
361static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
362{
363 struct nand_chip *this;
364 struct resource *r;
365 int err = 0;
366
367 printk(gBanner);
368
369 /* Allocate memory for MTD device structure and private data */
370 board_mtd =
371 kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
372 GFP_KERNEL);
373 if (!board_mtd) {
374 printk(KERN_WARNING
375 "Unable to allocate NAND MTD device structure.\n");
376 return -ENOMEM;
377 }
378
379 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
380
381 if (!r)
382 return -ENXIO;
383
384 /* map physical adress */
385 bcm_umi_io_base = ioremap(r->start, r->end - r->start + 1);
386
387 if (!bcm_umi_io_base) {
388 printk(KERN_ERR "ioremap to access BCM UMI NAND chip failed\n");
389 kfree(board_mtd);
390 return -EIO;
391 }
392
393 /* Get pointer to private data */
394 this = (struct nand_chip *)(&board_mtd[1]);
395
396 /* Initialize structures */
397 memset((char *)board_mtd, 0, sizeof(struct mtd_info));
398 memset((char *)this, 0, sizeof(struct nand_chip));
399
400 /* Link the private data with the MTD structure */
401 board_mtd->priv = this;
402
403 /* Initialize the NAND hardware. */
404 if (bcm_umi_nand_inithw() < 0) {
405 printk(KERN_ERR "BCM UMI NAND chip could not be initialized\n");
406 iounmap(bcm_umi_io_base);
407 kfree(board_mtd);
408 return -EIO;
409 }
410
411 /* Set address of NAND IO lines */
412 this->IO_ADDR_W = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
413 this->IO_ADDR_R = bcm_umi_io_base + REG_NAND_DATA8_OFFSET;
414
415 /* Set command delay time, see datasheet for correct value */
416 this->chip_delay = 0;
417 /* Assign the device ready function, if available */
418 this->dev_ready = nand_dev_ready;
419 this->options = 0;
420
421 this->write_buf = bcm_umi_nand_write_buf;
422 this->read_buf = bcm_umi_nand_read_buf;
423 this->verify_buf = bcm_umi_nand_verify_buf;
424
425 this->cmd_ctrl = bcm_umi_nand_hwcontrol;
426 this->ecc.mode = NAND_ECC_HW;
427 this->ecc.size = 512;
428 this->ecc.bytes = NAND_ECC_NUM_BYTES;
429#if NAND_ECC_BCH
430 this->ecc.read_page = bcm_umi_bch_read_page_hwecc;
431 this->ecc.write_page = bcm_umi_bch_write_page_hwecc;
432#else
433 this->ecc.correct = nand_correct_data512;
434 this->ecc.calculate = bcm_umi_hamming_get_hw_ecc;
435 this->ecc.hwctl = bcm_umi_hamming_enable_hwecc;
436#endif
437
438#if USE_DMA
439 err = nand_dma_init();
440 if (err != 0)
441 return err;
442#endif
443
444 /* Figure out the size of the device that we have.
445 * We need to do this to figure out which ECC
446 * layout we'll be using.
447 */
448
449 err = nand_scan_ident(board_mtd, 1);
450 if (err) {
451 printk(KERN_ERR "nand_scan failed: %d\n", err);
452 iounmap(bcm_umi_io_base);
453 kfree(board_mtd);
454 return err;
455 }
456
457 /* Now that we know the nand size, we can setup the ECC layout */
458
459 switch (board_mtd->writesize) { /* writesize is the pagesize */
460 case 4096:
461 this->ecc.layout = &nand_hw_eccoob_4096;
462 break;
463 case 2048:
464 this->ecc.layout = &nand_hw_eccoob_2048;
465 break;
466 case 512:
467 this->ecc.layout = &nand_hw_eccoob_512;
468 break;
469 default:
470 {
471 printk(KERN_ERR "NAND - Unrecognized pagesize: %d\n",
472 board_mtd->writesize);
473 return -EINVAL;
474 }
475 }
476
477#if NAND_ECC_BCH
478 if (board_mtd->writesize > 512) {
479 if (this->options & NAND_USE_FLASH_BBT)
480 largepage_bbt.options = NAND_BBT_SCAN2NDPAGE;
481 this->badblock_pattern = &largepage_bbt;
482 }
483#endif
484
485 /* Now finish off the scan, now that ecc.layout has been initialized. */
486
487 err = nand_scan_tail(board_mtd);
488 if (err) {
489 printk(KERN_ERR "nand_scan failed: %d\n", err);
490 iounmap(bcm_umi_io_base);
491 kfree(board_mtd);
492 return err;
493 }
494
495 /* Register the partitions */
496 {
497 int nr_partitions;
498 struct mtd_partition *partition_info;
499
500 board_mtd->name = "bcm_umi-nand";
501 nr_partitions =
502 parse_mtd_partitions(board_mtd, part_probes,
503 &partition_info, 0);
504
505 if (nr_partitions <= 0) {
506 printk(KERN_ERR "BCM UMI NAND: Too few partitions - %d\n",
507 nr_partitions);
508 iounmap(bcm_umi_io_base);
509 kfree(board_mtd);
510 return -EIO;
511 }
512 add_mtd_partitions(board_mtd, partition_info, nr_partitions);
513 }
514
515 /* Return happy */
516 return 0;
517}
518
519static int bcm_umi_nand_remove(struct platform_device *pdev)
520{
521#if USE_DMA
522 nand_dma_term();
523#endif
524
525 /* Release resources, unregister device */
526 nand_release(board_mtd);
527
528 /* unmap physical adress */
529 iounmap(bcm_umi_io_base);
530
531 /* Free the MTD device structure */
532 kfree(board_mtd);
533
534 return 0;
535}
536
537#ifdef CONFIG_PM
538static int bcm_umi_nand_suspend(struct platform_device *pdev,
539 pm_message_t state)
540{
541 printk(KERN_ERR "MTD NAND suspend is being called\n");
542 return 0;
543}
544
545static int bcm_umi_nand_resume(struct platform_device *pdev)
546{
547 printk(KERN_ERR "MTD NAND resume is being called\n");
548 return 0;
549}
550#else
551#define bcm_umi_nand_suspend NULL
552#define bcm_umi_nand_resume NULL
553#endif
554
555static struct platform_driver nand_driver = {
556 .driver = {
557 .name = "bcm-nand",
558 .owner = THIS_MODULE,
559 },
560 .probe = bcm_umi_nand_probe,
561 .remove = bcm_umi_nand_remove,
562 .suspend = bcm_umi_nand_suspend,
563 .resume = bcm_umi_nand_resume,
564};
565
566static int __init nand_init(void)
567{
568 return platform_driver_register(&nand_driver);
569}
570
571static void __exit nand_exit(void)
572{
573 platform_driver_unregister(&nand_driver);
574}
575
576module_init(nand_init);
577module_exit(nand_exit);
578
579MODULE_LICENSE("GPL");
580MODULE_AUTHOR("Broadcom");
581MODULE_DESCRIPTION("BCM UMI MTD NAND driver");
diff --git a/drivers/mtd/nand/nand_bcm_umi.c b/drivers/mtd/nand/nand_bcm_umi.c
new file mode 100644
index 000000000000..46a6bc9c4b74
--- /dev/null
+++ b/drivers/mtd/nand/nand_bcm_umi.c
@@ -0,0 +1,149 @@
1/*****************************************************************************
2* Copyright 2004 - 2009 Broadcom Corporation. All rights reserved.
3*
4* Unless you and Broadcom execute a separate written software license
5* agreement governing use of this software, this software is licensed to you
6* under the terms of the GNU General Public License version 2, available at
7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8*
9* Notwithstanding the above, under no circumstances may you combine this
10* software in any way with any other Broadcom software provided under a
11* license other than the GPL, without Broadcom's express prior written
12* consent.
13*****************************************************************************/
14
15/* ---- Include Files ---------------------------------------------------- */
16#include <mach/reg_umi.h>
17#include "nand_bcm_umi.h"
18#ifdef BOOT0_BUILD
19#include <uart.h>
20#endif
21
22/* ---- External Variable Declarations ----------------------------------- */
23/* ---- External Function Prototypes ------------------------------------- */
24/* ---- Public Variables ------------------------------------------------- */
25/* ---- Private Constants and Types -------------------------------------- */
26/* ---- Private Function Prototypes -------------------------------------- */
27/* ---- Private Variables ------------------------------------------------ */
28/* ---- Private Functions ------------------------------------------------ */
29
30#if NAND_ECC_BCH
31/****************************************************************************
32* nand_bch_ecc_flip_bit - Routine to flip an errored bit
33*
34* PURPOSE:
35* This is a helper routine that flips the bit (0 -> 1 or 1 -> 0) of the
36* errored bit specified
37*
38* PARAMETERS:
39* datap - Container that holds the 512 byte data
40* errorLocation - Location of the bit that needs to be flipped
41*
42* RETURNS:
43* None
44****************************************************************************/
45static void nand_bcm_umi_bch_ecc_flip_bit(uint8_t *datap, int errorLocation)
46{
47 int locWithinAByte = (errorLocation & REG_UMI_BCH_ERR_LOC_BYTE) >> 0;
48 int locWithinAWord = (errorLocation & REG_UMI_BCH_ERR_LOC_WORD) >> 3;
49 int locWithinAPage = (errorLocation & REG_UMI_BCH_ERR_LOC_PAGE) >> 5;
50
51 uint8_t errorByte = 0;
52 uint8_t byteMask = 1 << locWithinAByte;
53
54 /* BCH uses big endian, need to change the location
55 * bits to little endian */
56 locWithinAWord = 3 - locWithinAWord;
57
58 errorByte = datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord];
59
60#ifdef BOOT0_BUILD
61 puthexs("\nECC Correct Offset: ",
62 locWithinAPage * sizeof(uint32_t) + locWithinAWord);
63 puthexs(" errorByte:", errorByte);
64 puthex8(" Bit: ", locWithinAByte);
65#endif
66
67 if (errorByte & byteMask) {
68 /* bit needs to be cleared */
69 errorByte &= ~byteMask;
70 } else {
71 /* bit needs to be set */
72 errorByte |= byteMask;
73 }
74
75 /* write back the value with the fixed bit */
76 datap[locWithinAPage * sizeof(uint32_t) + locWithinAWord] = errorByte;
77}
78
79/****************************************************************************
80* nand_correct_page_bch - Routine to correct bit errors when reading NAND
81*
82* PURPOSE:
83* This routine reads the BCH registers to determine if there are any bit
84* errors during the read of the last 512 bytes of data + ECC bytes. If
85* errors exists, the routine fixes it.
86*
87* PARAMETERS:
88* datap - Container that holds the 512 byte data
89*
90* RETURNS:
91* 0 or greater = Number of errors corrected
92* (No errors are found or errors have been fixed)
93* -1 = Error(s) cannot be fixed
94****************************************************************************/
95int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
96 int numEccBytes)
97{
98 int numErrors;
99 int errorLocation;
100 int idx;
101 uint32_t regValue;
102
103 /* wait for read ECC to be valid */
104 regValue = nand_bcm_umi_bch_poll_read_ecc_calc();
105
106 /*
107 * read the control status register to determine if there
108 * are error'ed bits
109 * see if errors are correctible
110 */
111 if ((regValue & REG_UMI_BCH_CTRL_STATUS_UNCORR_ERR) > 0) {
112 int i;
113
114 for (i = 0; i < numEccBytes; i++) {
115 if (readEccData[i] != 0xff) {
116 /* errors cannot be fixed, return -1 */
117 return -1;
118 }
119 }
120 /* If ECC is unprogrammed then we can't correct,
121 * assume everything OK */
122 return 0;
123 }
124
125 if ((regValue & REG_UMI_BCH_CTRL_STATUS_CORR_ERR) == 0) {
126 /* no errors */
127 return 0;
128 }
129
130 /*
131 * Fix errored bits by doing the following:
132 * 1. Read the number of errors in the control and status register
133 * 2. Read the error location registers that corresponds to the number
134 * of errors reported
135 * 3. Invert the bit in the data
136 */
137 numErrors = (regValue & REG_UMI_BCH_CTRL_STATUS_NB_CORR_ERROR) >> 20;
138
139 for (idx = 0; idx < numErrors; idx++) {
140 errorLocation =
141 REG_UMI_BCH_ERR_LOC_ADDR(idx) & REG_UMI_BCH_ERR_LOC_MASK;
142
143 /* Flip bit */
144 nand_bcm_umi_bch_ecc_flip_bit(datap, errorLocation);
145 }
146 /* Errors corrected */
147 return numErrors;
148}
149#endif
diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h
new file mode 100644
index 000000000000..7cec2cd97854
--- /dev/null
+++ b/drivers/mtd/nand/nand_bcm_umi.h
@@ -0,0 +1,358 @@
1/*****************************************************************************
2* Copyright 2003 - 2009 Broadcom Corporation. All rights reserved.
3*
4* Unless you and Broadcom execute a separate written software license
5* agreement governing use of this software, this software is licensed to you
6* under the terms of the GNU General Public License version 2, available at
7* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8*
9* Notwithstanding the above, under no circumstances may you combine this
10* software in any way with any other Broadcom software provided under a
11* license other than the GPL, without Broadcom's express prior written
12* consent.
13*****************************************************************************/
14#ifndef NAND_BCM_UMI_H
15#define NAND_BCM_UMI_H
16
17/* ---- Include Files ---------------------------------------------------- */
18#include <mach/reg_umi.h>
19#include <mach/reg_nand.h>
20#include <cfg_global.h>
21
22/* ---- Constants and Types ---------------------------------------------- */
23#if (CFG_GLOBAL_CHIP_FAMILY == CFG_GLOBAL_CHIP_FAMILY_BCMRING)
24#define NAND_ECC_BCH (CFG_GLOBAL_CHIP_REV > 0xA0)
25#else
26#define NAND_ECC_BCH 0
27#endif
28
29#define CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES 13
30
31#if NAND_ECC_BCH
32#ifdef BOOT0_BUILD
33#define NAND_ECC_NUM_BYTES 13
34#else
35#define NAND_ECC_NUM_BYTES CFG_GLOBAL_NAND_ECC_BCH_NUM_BYTES
36#endif
37#else
38#define NAND_ECC_NUM_BYTES 3
39#endif
40
41#define NAND_DATA_ACCESS_SIZE 512
42
43/* ---- Variable Externs ------------------------------------------ */
44/* ---- Function Prototypes --------------------------------------- */
45int nand_bcm_umi_bch_correct_page(uint8_t *datap, uint8_t *readEccData,
46 int numEccBytes);
47
48/* Check in device is ready */
49static inline int nand_bcm_umi_dev_ready(void)
50{
51 return REG_UMI_NAND_RCSR & REG_UMI_NAND_RCSR_RDY;
52}
53
54/* Wait until device is ready */
55static inline void nand_bcm_umi_wait_till_ready(void)
56{
57 while (nand_bcm_umi_dev_ready() == 0)
58 ;
59}
60
61/* Enable Hamming ECC */
62static inline void nand_bcm_umi_hamming_enable_hwecc(void)
63{
64 /* disable and reset ECC, 512 byte page */
65 REG_UMI_NAND_ECC_CSR &= ~(REG_UMI_NAND_ECC_CSR_ECC_ENABLE |
66 REG_UMI_NAND_ECC_CSR_256BYTE);
67 /* enable ECC */
68 REG_UMI_NAND_ECC_CSR |= REG_UMI_NAND_ECC_CSR_ECC_ENABLE;
69}
70
71#if NAND_ECC_BCH
72/* BCH ECC specifics */
73#define ECC_BITS_PER_CORRECTABLE_BIT 13
74
75/* Enable BCH Read ECC */
76static inline void nand_bcm_umi_bch_enable_read_hwecc(void)
77{
78 /* disable and reset ECC */
79 REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID;
80 /* Turn on ECC */
81 REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN;
82}
83
84/* Enable BCH Write ECC */
85static inline void nand_bcm_umi_bch_enable_write_hwecc(void)
86{
87 /* disable and reset ECC */
88 REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID;
89 /* Turn on ECC */
90 REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_WR_EN;
91}
92
93/* Config number of BCH ECC bytes */
94static inline void nand_bcm_umi_bch_config_ecc(uint8_t numEccBytes)
95{
96 uint32_t nValue;
97 uint32_t tValue;
98 uint32_t kValue;
99 uint32_t numBits = numEccBytes * 8;
100
101 /* disable and reset ECC */
102 REG_UMI_BCH_CTRL_STATUS =
103 REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID |
104 REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID;
105
106 /* Every correctible bit requires 13 ECC bits */
107 tValue = (uint32_t) (numBits / ECC_BITS_PER_CORRECTABLE_BIT);
108
109 /* Total data in number of bits for generating and computing BCH ECC */
110 nValue = (NAND_DATA_ACCESS_SIZE + numEccBytes) * 8;
111
112 /* K parameter is used internally. K = N - (T * 13) */
113 kValue = nValue - (tValue * ECC_BITS_PER_CORRECTABLE_BIT);
114
115 /* Write the settings */
116 REG_UMI_BCH_N = nValue;
117 REG_UMI_BCH_T = tValue;
118 REG_UMI_BCH_K = kValue;
119}
120
121/* Pause during ECC read calculation to skip bytes in OOB */
122static inline void nand_bcm_umi_bch_pause_read_ecc_calc(void)
123{
124 REG_UMI_BCH_CTRL_STATUS =
125 REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN |
126 REG_UMI_BCH_CTRL_STATUS_PAUSE_ECC_DEC;
127}
128
129/* Resume during ECC read calculation after skipping bytes in OOB */
130static inline void nand_bcm_umi_bch_resume_read_ecc_calc(void)
131{
132 REG_UMI_BCH_CTRL_STATUS = REG_UMI_BCH_CTRL_STATUS_ECC_RD_EN;
133}
134
135/* Poll read ECC calc to check when hardware completes */
136static inline uint32_t nand_bcm_umi_bch_poll_read_ecc_calc(void)
137{
138 uint32_t regVal;
139
140 do {
141 /* wait for ECC to be valid */
142 regVal = REG_UMI_BCH_CTRL_STATUS;
143 } while ((regVal & REG_UMI_BCH_CTRL_STATUS_RD_ECC_VALID) == 0);
144
145 return regVal;
146}
147
148/* Poll write ECC calc to check when hardware completes */
149static inline void nand_bcm_umi_bch_poll_write_ecc_calc(void)
150{
151 /* wait for ECC to be valid */
152 while ((REG_UMI_BCH_CTRL_STATUS & REG_UMI_BCH_CTRL_STATUS_WR_ECC_VALID)
153 == 0)
154 ;
155}
156
157/* Read the OOB and ECC, for kernel write OOB to a buffer */
158#if defined(__KERNEL__) && !defined(STANDALONE)
159static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
160 uint8_t *eccCalc, int numEccBytes, uint8_t *oobp)
161#else
162static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
163 uint8_t *eccCalc, int numEccBytes)
164#endif
165{
166 int eccPos = 0;
167 int numToRead = 16; /* There are 16 bytes per sector in the OOB */
168
169 /* ECC is already paused when this function is called */
170
171 if (pageSize == NAND_DATA_ACCESS_SIZE) {
172 while (numToRead > numEccBytes) {
173 /* skip free oob region */
174#if defined(__KERNEL__) && !defined(STANDALONE)
175 *oobp++ = REG_NAND_DATA8;
176#else
177 REG_NAND_DATA8;
178#endif
179 numToRead--;
180 }
181
182 /* read ECC bytes before BI */
183 nand_bcm_umi_bch_resume_read_ecc_calc();
184
185 while (numToRead > 11) {
186#if defined(__KERNEL__) && !defined(STANDALONE)
187 *oobp = REG_NAND_DATA8;
188 eccCalc[eccPos++] = *oobp;
189 oobp++;
190#else
191 eccCalc[eccPos++] = REG_NAND_DATA8;
192#endif
193 }
194
195 nand_bcm_umi_bch_pause_read_ecc_calc();
196
197 if (numToRead == 11) {
198 /* read BI */
199#if defined(__KERNEL__) && !defined(STANDALONE)
200 *oobp++ = REG_NAND_DATA8;
201#else
202 REG_NAND_DATA8;
203#endif
204 numToRead--;
205 }
206
207 /* read ECC bytes */
208 nand_bcm_umi_bch_resume_read_ecc_calc();
209 while (numToRead) {
210#if defined(__KERNEL__) && !defined(STANDALONE)
211 *oobp = REG_NAND_DATA8;
212 eccCalc[eccPos++] = *oobp;
213 oobp++;
214#else
215 eccCalc[eccPos++] = REG_NAND_DATA8;
216#endif
217 numToRead--;
218 }
219 } else {
220 /* skip BI */
221#if defined(__KERNEL__) && !defined(STANDALONE)
222 *oobp++ = REG_NAND_DATA8;
223#else
224 REG_NAND_DATA8;
225#endif
226 numToRead--;
227
228 while (numToRead > numEccBytes) {
229 /* skip free oob region */
230#if defined(__KERNEL__) && !defined(STANDALONE)
231 *oobp++ = REG_NAND_DATA8;
232#else
233 REG_NAND_DATA8;
234#endif
235 numToRead--;
236 }
237
238 /* read ECC bytes */
239 nand_bcm_umi_bch_resume_read_ecc_calc();
240 while (numToRead) {
241#if defined(__KERNEL__) && !defined(STANDALONE)
242 *oobp = REG_NAND_DATA8;
243 eccCalc[eccPos++] = *oobp;
244 oobp++;
245#else
246 eccCalc[eccPos++] = REG_NAND_DATA8;
247#endif
248 numToRead--;
249 }
250 }
251}
252
253/* Helper function to write ECC */
254static inline void NAND_BCM_UMI_ECC_WRITE(int numEccBytes, int eccBytePos,
255 uint8_t *oobp, uint8_t eccVal)
256{
257 if (eccBytePos <= numEccBytes)
258 *oobp = eccVal;
259}
260
261/* Write OOB with ECC */
262static inline void nand_bcm_umi_bch_write_oobEcc(uint32_t pageSize,
263 uint8_t *oobp, int numEccBytes)
264{
265 uint32_t eccVal = 0xffffffff;
266
267 /* wait for write ECC to be valid */
268 nand_bcm_umi_bch_poll_write_ecc_calc();
269
270 /*
271 ** Get the hardware ecc from the 32-bit result registers.
272 ** Read after 512 byte accesses. Format B3B2B1B0
273 ** where B3 = ecc3, etc.
274 */
275
276 if (pageSize == NAND_DATA_ACCESS_SIZE) {
277 /* Now fill in the ECC bytes */
278 if (numEccBytes >= 13)
279 eccVal = REG_UMI_BCH_WR_ECC_3;
280
281 /* Usually we skip CM in oob[0,1] */
282 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[0],
283 (eccVal >> 16) & 0xff);
284 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[1],
285 (eccVal >> 8) & 0xff);
286
287 /* Write ECC in oob[2,3,4] */
288 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[2],
289 eccVal & 0xff); /* ECC 12 */
290
291 if (numEccBytes >= 9)
292 eccVal = REG_UMI_BCH_WR_ECC_2;
293
294 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[3],
295 (eccVal >> 24) & 0xff); /* ECC11 */
296 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[4],
297 (eccVal >> 16) & 0xff); /* ECC10 */
298
299 /* Always Skip BI in oob[5] */
300 } else {
301 /* Always Skip BI in oob[0] */
302
303 /* Now fill in the ECC bytes */
304 if (numEccBytes >= 13)
305 eccVal = REG_UMI_BCH_WR_ECC_3;
306
307 /* Usually skip CM in oob[1,2] */
308 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 15, &oobp[1],
309 (eccVal >> 16) & 0xff);
310 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 14, &oobp[2],
311 (eccVal >> 8) & 0xff);
312
313 /* Write ECC in oob[3-15] */
314 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 13, &oobp[3],
315 eccVal & 0xff); /* ECC12 */
316
317 if (numEccBytes >= 9)
318 eccVal = REG_UMI_BCH_WR_ECC_2;
319
320 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 12, &oobp[4],
321 (eccVal >> 24) & 0xff); /* ECC11 */
322 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 11, &oobp[5],
323 (eccVal >> 16) & 0xff); /* ECC10 */
324 }
325
326 /* Fill in the remainder of ECC locations */
327 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 10, &oobp[6],
328 (eccVal >> 8) & 0xff); /* ECC9 */
329 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 9, &oobp[7],
330 eccVal & 0xff); /* ECC8 */
331
332 if (numEccBytes >= 5)
333 eccVal = REG_UMI_BCH_WR_ECC_1;
334
335 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 8, &oobp[8],
336 (eccVal >> 24) & 0xff); /* ECC7 */
337 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 7, &oobp[9],
338 (eccVal >> 16) & 0xff); /* ECC6 */
339 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 6, &oobp[10],
340 (eccVal >> 8) & 0xff); /* ECC5 */
341 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 5, &oobp[11],
342 eccVal & 0xff); /* ECC4 */
343
344 if (numEccBytes >= 1)
345 eccVal = REG_UMI_BCH_WR_ECC_0;
346
347 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 4, &oobp[12],
348 (eccVal >> 24) & 0xff); /* ECC3 */
349 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 3, &oobp[13],
350 (eccVal >> 16) & 0xff); /* ECC2 */
351 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 2, &oobp[14],
352 (eccVal >> 8) & 0xff); /* ECC1 */
353 NAND_BCM_UMI_ECC_WRITE(numEccBytes, 1, &oobp[15],
354 eccVal & 0xff); /* ECC0 */
355}
356#endif
357
358#endif /* NAND_BCM_UMI_H */