Merge branch 'for-3.5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[firefly-linux-kernel-4.4.55.git] / drivers / mtd / nand / bcm_umi_bch.c
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 -------------------------------------- */
24 static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
25         struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
26 static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
27         struct nand_chip *chip, const uint8_t *buf, int oob_required);
28
29 /* ---- Private Variables ------------------------------------------------ */
30
31 /*
32 ** nand_hw_eccoob
33 ** New oob placement block for use with hardware ecc generation.
34 */
35 static 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 */
51 static 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. */
72 static 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 *  @oob_required:       caller expects OOB data read to chip->oob_poi
107 *
108 ***************************************************************************/
109 static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
110                                        struct nand_chip *chip, uint8_t * buf,
111                                        int oob_required, int page)
112 {
113         int sectorIdx = 0;
114         int eccsize = chip->ecc.size;
115         int eccsteps = chip->ecc.steps;
116         uint8_t *datap = buf;
117         uint8_t eccCalc[NAND_ECC_NUM_BYTES];
118         int sectorOobSize = mtd->oobsize / eccsteps;
119         int stat;
120         unsigned int max_bitflips = 0;
121
122         for (sectorIdx = 0; sectorIdx < eccsteps;
123                         sectorIdx++, datap += eccsize) {
124                 if (sectorIdx > 0) {
125                         /* Seek to page location within sector */
126                         chip->cmdfunc(mtd, NAND_CMD_RNDOUT, sectorIdx * eccsize,
127                                       -1);
128                 }
129
130                 /* Enable hardware ECC before reading the buf */
131                 nand_bcm_umi_bch_enable_read_hwecc();
132
133                 /* Read in data */
134                 bcm_umi_nand_read_buf(mtd, datap, eccsize);
135
136                 /* Pause hardware ECC after reading the buf */
137                 nand_bcm_umi_bch_pause_read_ecc_calc();
138
139                 /* Read the OOB ECC */
140                 chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
141                               mtd->writesize + sectorIdx * sectorOobSize, -1);
142                 nand_bcm_umi_bch_read_oobEcc(mtd->writesize, eccCalc,
143                                              NAND_ECC_NUM_BYTES,
144                                              chip->oob_poi +
145                                              sectorIdx * sectorOobSize);
146
147                 /* Correct any ECC detected errors */
148                 stat =
149                     nand_bcm_umi_bch_correct_page(datap, eccCalc,
150                                                   NAND_ECC_NUM_BYTES);
151
152                 /* Update Stats */
153                 if (stat < 0) {
154 #if defined(NAND_BCM_UMI_DEBUG)
155                         printk(KERN_WARNING "%s uncorr_err sectorIdx=%d\n",
156                                __func__, sectorIdx);
157                         printk(KERN_WARNING
158                                "%s data %02x %02x %02x %02x "
159                                          "%02x %02x %02x %02x\n",
160                                __func__, datap[0], datap[1], datap[2], datap[3],
161                                datap[4], datap[5], datap[6], datap[7]);
162                         printk(KERN_WARNING
163                                "%s ecc  %02x %02x %02x %02x "
164                                          "%02x %02x %02x %02x %02x %02x "
165                                          "%02x %02x %02x\n",
166                                __func__, eccCalc[0], eccCalc[1], eccCalc[2],
167                                eccCalc[3], eccCalc[4], eccCalc[5], eccCalc[6],
168                                eccCalc[7], eccCalc[8], eccCalc[9], eccCalc[10],
169                                eccCalc[11], eccCalc[12]);
170                         BUG();
171 #endif
172                         mtd->ecc_stats.failed++;
173                 } else {
174 #if defined(NAND_BCM_UMI_DEBUG)
175                         if (stat > 0) {
176                                 printk(KERN_INFO
177                                        "%s %d correctable_errors detected\n",
178                                        __func__, stat);
179                         }
180 #endif
181                         mtd->ecc_stats.corrected += stat;
182                         max_bitflips = max_t(unsigned int, max_bitflips, stat);
183                 }
184         }
185         return max_bitflips;
186 }
187
188 /****************************************************************************
189 *
190 *  bcm_umi_bch_write_page_hwecc - hardware ecc based page write function
191 *  @mtd:        mtd info structure
192 *  @chip:       nand chip info structure
193 *  @buf:        data buffer
194 *  @oob_required:       must write chip->oob_poi to OOB
195 *
196 ***************************************************************************/
197 static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
198         struct nand_chip *chip, const uint8_t *buf, int oob_required)
199 {
200         int sectorIdx = 0;
201         int eccsize = chip->ecc.size;
202         int eccsteps = chip->ecc.steps;
203         const uint8_t *datap = buf;
204         uint8_t *oobp = chip->oob_poi;
205         int sectorOobSize = mtd->oobsize / eccsteps;
206
207         for (sectorIdx = 0; sectorIdx < eccsteps;
208              sectorIdx++, datap += eccsize, oobp += sectorOobSize) {
209                 /* Enable hardware ECC before writing the buf */
210                 nand_bcm_umi_bch_enable_write_hwecc();
211                 bcm_umi_nand_write_buf(mtd, datap, eccsize);
212                 nand_bcm_umi_bch_write_oobEcc(mtd->writesize, oobp,
213                                               NAND_ECC_NUM_BYTES);
214         }
215
216         bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
217 }