kernel: update 4.1 to 4.1.5
[oweals/openwrt.git] / target / linux / sunxi / patches-4.1 / 123-mtd-nand-sunxi-add-hw-randomizer-support.patch
1 From ef4bc8ab68979e5c1c30f061c5af1a7d6ec8eb52 Mon Sep 17 00:00:00 2001
2 From: Boris Brezillon <boris.brezillon@free-electrons.com>
3 Date: Tue, 21 Oct 2014 14:40:42 +0200
4 Subject: [PATCH] mtd: nand: sunxi: Add HW randomizer support
5
6 Add support for the HW randomizer available on the sunxi nand controller.
7
8 Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
9 Signed-off-by: Hans de Goede <hdegoede@redhat.com>
10 ---
11  drivers/mtd/nand/sunxi_nand.c | 603 ++++++++++++++++++++++++++++++++++++++++--
12  1 file changed, 585 insertions(+), 18 deletions(-)
13
14 --- a/drivers/mtd/nand/sunxi_nand.c
15 +++ b/drivers/mtd/nand/sunxi_nand.c
16 @@ -206,10 +206,12 @@ struct sunxi_nand_hw_ecc {
17   *
18   * @part: base paritition structure
19   * @ecc: per-partition ECC info
20 + * @rnd: per-partition randomizer info
21   */
22  struct sunxi_nand_part {
23         struct nand_part part;
24         struct nand_ecc_ctrl ecc;
25 +       struct nand_rnd_ctrl rnd;
26  };
27  
28  static inline struct sunxi_nand_part *
29 @@ -219,6 +221,29 @@ to_sunxi_nand_part(struct nand_part *par
30  }
31  
32  /*
33 + * sunxi NAND randomizer structure: stores NAND randomizer information
34 + *
35 + * @page: current page
36 + * @column: current column
37 + * @nseeds: seed table size
38 + * @seeds: seed table
39 + * @subseeds: pre computed sub seeds
40 + * @step: step function
41 + * @left: number of remaining bytes in the page
42 + * @state: current randomizer state
43 + */
44 +struct sunxi_nand_hw_rnd {
45 +       int page;
46 +       int column;
47 +       int nseeds;
48 +       u16 *seeds;
49 +       u16 *subseeds;
50 +       u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left);
51 +       int left;
52 +       u16 state;
53 +};
54 +
55 +/*
56   * NAND chip structure: stores NAND chip device related information
57   *
58   * @node:              used to store NAND chips into a list
59 @@ -233,6 +258,7 @@ struct sunxi_nand_chip {
60         struct list_head node;
61         struct nand_chip nand;
62         struct mtd_info mtd;
63 +       void *buffer;
64         unsigned long clk_rate;
65         int selected;
66         int nsels;
67 @@ -489,6 +515,185 @@ static void sunxi_nfc_write_buf(struct m
68         }
69  }
70  
71 +static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count)
72 +{
73 +       state &= 0x7fff;
74 +       count *= 8;
75 +       while (count--)
76 +               state = ((state >> 1) |
77 +                        ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
78 +
79 +       return state;
80 +}
81 +
82 +static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count)
83 +{
84 +       state &= 0x7fff;
85 +       while (count--)
86 +               state = ((state >> 1) |
87 +                        ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff;
88 +
89 +       return state;
90 +}
91 +
92 +static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column,
93 +                                 enum nand_rnd_action action)
94 +{
95 +       struct nand_chip *nand = mtd->priv;
96 +       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
97 +       struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
98 +       u16 state;
99 +
100 +       if (page < 0 && column < 0) {
101 +               rnd->page = -1;
102 +               rnd->column = -1;
103 +               return 0;
104 +       }
105 +
106 +       if (column < 0)
107 +               column = 0;
108 +       if (page < 0)
109 +               page = rnd->page;
110 +
111 +       if (page < 0)
112 +               return -EINVAL;
113 +
114 +       if (page != rnd->page && action == NAND_RND_READ) {
115 +               int status;
116 +
117 +               status = nand_page_get_status(mtd, page);
118 +               if (status == NAND_PAGE_STATUS_UNKNOWN) {
119 +                       nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
120 +                       sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
121 +                                          mtd->writesize + mtd->oobsize);
122 +
123 +                       if (nand_page_is_empty(mtd, sunxi_nand->buffer,
124 +                                              sunxi_nand->buffer +
125 +                                              mtd->writesize))
126 +                               status = NAND_PAGE_EMPTY;
127 +                       else
128 +                               status = NAND_PAGE_FILLED;
129 +
130 +                       nand_page_set_status(mtd, page, status);
131 +                       nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1);
132 +               }
133 +       }
134 +
135 +       state = rnd->seeds[page % rnd->nseeds];
136 +       rnd->page = page;
137 +       rnd->column = column;
138 +
139 +       if (rnd->step) {
140 +               rnd->state = rnd->step(mtd, state, column, &rnd->left);
141 +       } else {
142 +               rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096);
143 +               rnd->left = mtd->oobsize + mtd->writesize - column;
144 +       }
145 +
146 +       return 0;
147 +}
148 +
149 +static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf,
150 +                                     int len)
151 +{
152 +       struct nand_chip *nand = mtd->priv;
153 +       struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
154 +       struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
155 +       u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
156 +       int cnt;
157 +       int offs = 0;
158 +       int rndactiv;
159 +
160 +       tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN);
161 +       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
162 +
163 +       if (rnd->page < 0) {
164 +               sunxi_nfc_write_buf(mtd, buf, len);
165 +               return;
166 +       }
167 +
168 +       while (len > offs) {
169 +               cnt = len - offs;
170 +               if (cnt > 1024)
171 +                       cnt = 1024;
172 +
173 +               rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column,
174 +                                            &cnt);
175 +               if (rndactiv > 0) {
176 +                       writel(tmp | NFC_RANDOM_EN | (rnd->state << 16),
177 +                              nfc->regs + NFC_REG_ECC_CTL);
178 +                       if (rnd->left < cnt)
179 +                               cnt = rnd->left;
180 +               }
181 +
182 +               sunxi_nfc_write_buf(mtd, buf + offs, cnt);
183 +
184 +               if (rndactiv > 0)
185 +                       writel(tmp & ~NFC_RANDOM_EN,
186 +                              nfc->regs + NFC_REG_ECC_CTL);
187 +
188 +               offs += cnt;
189 +               if (len <= offs)
190 +                       break;
191 +
192 +               sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE);
193 +       }
194 +}
195 +
196 +static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf,
197 +                                    int len)
198 +{
199 +       struct nand_chip *nand = mtd->priv;
200 +       struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
201 +       struct sunxi_nand_hw_rnd *rnd = nand->cur_rnd->priv;
202 +       u32 tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
203 +       int cnt;
204 +       int offs = 0;
205 +       int rndactiv;
206 +
207 +       tmp &= ~(NFC_RANDOM_DIRECTION | NFC_RANDOM_SEED | NFC_RANDOM_EN);
208 +       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
209 +
210 +       if (rnd->page < 0) {
211 +               sunxi_nfc_read_buf(mtd, buf, len);
212 +               return;
213 +       }
214 +
215 +       while (len > offs) {
216 +               cnt = len - offs;
217 +               if (cnt > 1024)
218 +                       cnt = 1024;
219 +
220 +               if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY &&
221 +                   nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0)
222 +                       rndactiv = 1;
223 +               else
224 +                       rndactiv = 0;
225 +
226 +               if (rndactiv > 0) {
227 +                       writel(tmp | NFC_RANDOM_EN | (rnd->state << 16),
228 +                              nfc->regs + NFC_REG_ECC_CTL);
229 +                       if (rnd->left < cnt)
230 +                               cnt = rnd->left;
231 +               }
232 +
233 +               if (buf)
234 +                       sunxi_nfc_read_buf(mtd, buf + offs, cnt);
235 +               else
236 +                       sunxi_nfc_read_buf(mtd, NULL, cnt);
237 +
238 +               if (rndactiv > 0)
239 +                       writel(tmp & ~NFC_RANDOM_EN,
240 +                              nfc->regs + NFC_REG_ECC_CTL);
241 +
242 +               offs += cnt;
243 +               if (len <= offs)
244 +                       break;
245 +
246 +               sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ);
247 +       }
248 +}
249 +
250  static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
251  {
252         uint8_t ret;
253 @@ -538,16 +743,43 @@ static int sunxi_nfc_hw_ecc_read_page(st
254                                       int oob_required, int page)
255  {
256         struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
257 +       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
258         struct nand_ecc_ctrl *ecc = chip->cur_ecc;
259         struct nand_ecclayout *layout = ecc->layout;
260         struct sunxi_nand_hw_ecc *data = ecc->priv;
261         unsigned int max_bitflips = 0;
262 +       int status;
263         int offset;
264         int ret;
265         u32 tmp;
266         int i;
267         int cnt;
268  
269 +       status = nand_page_get_status(mtd, page);
270 +       if (status == NAND_PAGE_STATUS_UNKNOWN) {
271 +               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
272 +               sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
273 +                                  mtd->writesize + mtd->oobsize);
274 +
275 +               if (nand_page_is_empty(mtd, sunxi_nand->buffer,
276 +                                      sunxi_nand->buffer +
277 +                                      mtd->writesize)) {
278 +                       status = NAND_PAGE_EMPTY;
279 +               } else {
280 +                       status = NAND_PAGE_FILLED;
281 +                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
282 +               }
283 +
284 +               nand_page_set_status(mtd, page, status);
285 +       }
286 +
287 +       if (status == NAND_PAGE_EMPTY) {
288 +               memset(buf, 0xff, mtd->writesize);
289 +               if (oob_required)
290 +                       memset(chip->oob_poi, 0xff, mtd->oobsize);
291 +               return 0;
292 +       }
293 +
294         tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
295         tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
296         tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
297 @@ -556,12 +788,15 @@ static int sunxi_nfc_hw_ecc_read_page(st
298         writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
299  
300         for (i = 0; i < ecc->steps; i++) {
301 +               bool rndactiv = false;
302 +
303                 if (i)
304                         chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
305  
306                 offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
307  
308 -               chip->read_buf(mtd, NULL, ecc->size);
309 +               nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ);
310 +               nand_rnd_read_buf(mtd, NULL, ecc->size);
311  
312                 chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
313  
314 @@ -569,6 +804,25 @@ static int sunxi_nfc_hw_ecc_read_page(st
315                 if (ret)
316                         return ret;
317  
318 +               if (i) {
319 +                       cnt = ecc->bytes + 4;
320 +                       if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 &&
321 +                           cnt == ecc->bytes + 4)
322 +                               rndactiv = true;
323 +               } else {
324 +                       cnt = ecc->bytes + 2;
325 +                       if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 &&
326 +                           cnt == ecc->bytes + 2)
327 +                               rndactiv = true;
328 +               }
329 +
330 +               if (rndactiv) {
331 +                       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
332 +                       tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
333 +                       tmp |= NFC_RANDOM_EN;
334 +                       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
335 +               }
336 +
337                 tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
338                 writel(tmp, nfc->regs + NFC_REG_CMD);
339  
340 @@ -579,6 +833,9 @@ static int sunxi_nfc_hw_ecc_read_page(st
341                 memcpy_fromio(buf + (i * ecc->size),
342                               nfc->regs + NFC_RAM0_BASE, ecc->size);
343  
344 +               writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
345 +                      nfc->regs + NFC_REG_ECC_CTL);
346 +
347                 if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
348                         mtd->ecc_stats.failed++;
349                 } else {
350 @@ -594,9 +851,10 @@ static int sunxi_nfc_hw_ecc_read_page(st
351                         if (ret)
352                                 return ret;
353  
354 +                       nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
355                         offset -= mtd->writesize;
356 -                       chip->read_buf(mtd, chip->oob_poi + offset,
357 -                                     ecc->bytes + 4);
358 +                       nand_rnd_read_buf(mtd, chip->oob_poi + offset,
359 +                                         ecc->bytes + 4);
360                 }
361         }
362  
363 @@ -606,11 +864,14 @@ static int sunxi_nfc_hw_ecc_read_page(st
364                         offset = mtd->writesize +
365                                  ecc->layout->oobfree[ecc->steps].offset;
366                         chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
367 +                       nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
368                         offset -= mtd->writesize;
369 -                       chip->read_buf(mtd, chip->oob_poi + offset, cnt);
370 +                       nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt);
371                 }
372         }
373  
374 +       nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
375 +
376         tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
377         tmp &= ~NFC_ECC_EN;
378  
379 @@ -627,6 +888,7 @@ static int sunxi_nfc_hw_ecc_write_page(s
380         struct nand_ecc_ctrl *ecc = chip->cur_ecc;
381         struct nand_ecclayout *layout = ecc->layout;
382         struct sunxi_nand_hw_ecc *data = ecc->priv;
383 +       struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
384         int offset;
385         int ret;
386         u32 tmp;
387 @@ -641,22 +903,56 @@ static int sunxi_nfc_hw_ecc_write_page(s
388         writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
389  
390         for (i = 0; i < ecc->steps; i++) {
391 +               bool rndactiv = false;
392 +               u8 oob_buf[4];
393 +
394                 if (i)
395                         chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
396  
397 -               chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
398 +               nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE);
399 +               nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size);
400  
401                 offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
402  
403                 /* Fill OOB data in */
404 -               if (oob_required) {
405 -                       tmp = 0xffffffff;
406 -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
407 -                                   4);
408 +               if (!oob_required)
409 +                       memset(oob_buf, 0xff, 4);
410 +               else
411 +                       memcpy(oob_buf,
412 +                              chip->oob_poi + layout->oobfree[i].offset,
413 +                              4);
414 +
415 +
416 +               memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
417 +
418 +               if (i) {
419 +                       cnt = ecc->bytes + 4;
420 +                       if (rnd &&
421 +                           nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 &&
422 +                           cnt == ecc->bytes + 4)
423 +                               rndactiv = true;
424                 } else {
425 -                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
426 -                                   chip->oob_poi + offset - mtd->writesize,
427 -                                   4);
428 +                       cnt = ecc->bytes + 2;
429 +                       if (rnd &&
430 +                           nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 &&
431 +                           cnt == ecc->bytes + 2)
432 +                               rndactiv = true;
433 +               }
434 +
435 +               if (rndactiv) {
436 +                       /* pre randomize to generate FF patterns on the NAND */
437 +                       if (!i) {
438 +                               u16 state = rnd->subseeds[rnd->page % rnd->nseeds];
439 +                               state = sunxi_nfc_hwrnd_single_step(state, 15);
440 +                               oob_buf[0] ^= state;
441 +                               state = sunxi_nfc_hwrnd_step(rnd, state, 1);
442 +                               oob_buf[1] ^= state;
443 +                               memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob_buf, 4);
444 +                       }
445 +                       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
446 +                       tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
447 +                       tmp |= NFC_RANDOM_EN;
448 +                       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
449                 }
450  
451                 chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
452 @@ -671,6 +967,9 @@ static int sunxi_nfc_hw_ecc_write_page(s
453                 ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
454                 if (ret)
455                         return ret;
456 +
457 +               writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
458 +                      nfc->regs + NFC_REG_ECC_CTL);
459         }
460  
461         if (oob_required) {
462 @@ -679,11 +978,14 @@ static int sunxi_nfc_hw_ecc_write_page(s
463                         offset = mtd->writesize +
464                                  ecc->layout->oobfree[i].offset;
465                         chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
466 +                       nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
467                         offset -= mtd->writesize;
468 -                       chip->write_buf(mtd, chip->oob_poi + offset, cnt);
469 +                       nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt);
470                 }
471         }
472  
473 +       nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
474 +
475         tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
476         tmp &= ~NFC_ECC_EN;
477  
478 @@ -692,22 +994,76 @@ static int sunxi_nfc_hw_ecc_write_page(s
479         return 0;
480  }
481  
482 +static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state,
483 +                                     int column, int *left)
484 +{
485 +       struct nand_chip *chip = mtd->priv;
486 +       struct nand_ecc_ctrl *ecc = chip->cur_ecc;
487 +       struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
488 +       int nblks = mtd->writesize / ecc->size;
489 +       int modsize = ecc->size;
490 +       int steps;
491 +
492 +       if (column < mtd->writesize) {
493 +               steps = column % modsize;
494 +               *left = modsize - steps;
495 +       } else if (column < mtd->writesize +
496 +                           (nblks * (ecc->bytes + 4))) {
497 +               column -= mtd->writesize;
498 +               steps = column % (ecc->bytes + 4);
499 +               *left = ecc->bytes + 4 - steps;
500 +               state = rnd->subseeds[rnd->page % rnd->nseeds];
501 +       } else {
502 +               steps = column % 4096;
503 +               *left = mtd->writesize + mtd->oobsize - column;
504 +       }
505 +
506 +       return sunxi_nfc_hwrnd_step(rnd, state, steps);
507 +}
508 +
509  static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
510                                                struct nand_chip *chip,
511                                                uint8_t *buf, int oob_required,
512                                                int page)
513  {
514         struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
515 +       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip);
516         struct nand_ecc_ctrl *ecc = chip->cur_ecc;
517         struct sunxi_nand_hw_ecc *data = ecc->priv;
518         unsigned int max_bitflips = 0;
519         uint8_t *oob = chip->oob_poi;
520         int offset = 0;
521         int ret;
522 +       int status;
523         int cnt;
524         u32 tmp;
525         int i;
526  
527 +       status = nand_page_get_status(mtd, page);
528 +       if (status == NAND_PAGE_STATUS_UNKNOWN) {
529 +               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
530 +               sunxi_nfc_read_buf(mtd, sunxi_nand->buffer,
531 +                                  mtd->writesize + mtd->oobsize);
532 +
533 +               if (nand_page_is_empty(mtd, sunxi_nand->buffer,
534 +                                      sunxi_nand->buffer +
535 +                                      mtd->writesize)) {
536 +                       status = NAND_PAGE_EMPTY;
537 +               } else {
538 +                       status = NAND_PAGE_FILLED;
539 +                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
540 +               }
541 +
542 +               nand_page_set_status(mtd, page, status);
543 +       }
544 +
545 +       if (status == NAND_PAGE_EMPTY) {
546 +               memset(buf, 0xff, mtd->writesize);
547 +               if (oob_required)
548 +                       memset(chip->oob_poi, 0xff, mtd->oobsize);
549 +               return 0;
550 +       }
551 +
552         tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
553         tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
554         tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
555 @@ -716,7 +1072,17 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
556         writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
557  
558         for (i = 0; i < ecc->steps; i++) {
559 -               chip->read_buf(mtd, NULL, ecc->size);
560 +               nand_rnd_config(mtd, page, offset, NAND_RND_READ);
561 +               nand_rnd_read_buf(mtd, NULL, ecc->size);
562 +
563 +               cnt = ecc->bytes + 4;
564 +               if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 &&
565 +                   cnt == ecc->bytes + 4) {
566 +                       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
567 +                       tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
568 +                       tmp |= NFC_RANDOM_EN;
569 +                       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
570 +               }
571  
572                 tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
573                 writel(tmp, nfc->regs + NFC_REG_CMD);
574 @@ -729,6 +1095,9 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
575                 buf += ecc->size;
576                 offset += ecc->size;
577  
578 +               writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
579 +                      nfc->regs + NFC_REG_ECC_CTL);
580 +
581                 if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
582                         mtd->ecc_stats.failed++;
583                 } else {
584 @@ -739,7 +1108,8 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
585  
586                 if (oob_required) {
587                         chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
588 -                       chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad);
589 +                       nand_rnd_config(mtd, -1, offset, NAND_RND_READ);
590 +                       nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad);
591                         oob += ecc->bytes + ecc->prepad;
592                 }
593  
594 @@ -750,10 +1120,13 @@ static int sunxi_nfc_hw_syndrome_ecc_rea
595                 cnt = mtd->oobsize - (oob - chip->oob_poi);
596                 if (cnt > 0) {
597                         chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
598 -                       chip->read_buf(mtd, oob, cnt);
599 +                       nand_rnd_config(mtd, page, offset, NAND_RND_READ);
600 +                       nand_rnd_read_buf(mtd, oob, cnt);
601                 }
602         }
603  
604 +       nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
605 +
606         writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
607                nfc->regs + NFC_REG_ECC_CTL);
608  
609 @@ -768,6 +1141,7 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
610         struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
611         struct nand_ecc_ctrl *ecc = chip->cur_ecc;
612         struct sunxi_nand_hw_ecc *data = ecc->priv;
613 +       struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
614         uint8_t *oob = chip->oob_poi;
615         int offset = 0;
616         int ret;
617 @@ -783,7 +1157,8 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
618         writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
619  
620         for (i = 0; i < ecc->steps; i++) {
621 -               chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
622 +               nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
623 +               nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size);
624                 offset += ecc->size;
625  
626                 /* Fill OOB data in */
627 @@ -796,6 +1171,16 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
628                                     4);
629                 }
630  
631 +               cnt = ecc->bytes + 4;
632 +               if (rnd &&
633 +                   nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 &&
634 +                   cnt == ecc->bytes + 4) {
635 +                       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
636 +                       tmp &= ~(NFC_RANDOM_DIRECTION | NFC_ECC_EXCEPTION);
637 +                       tmp |= NFC_RANDOM_EN;
638 +                       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
639 +               }
640 +
641                 tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
642                       (1 << 30);
643                 writel(tmp, nfc->regs + NFC_REG_CMD);
644 @@ -804,6 +1189,9 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
645                 if (ret)
646                         return ret;
647  
648 +               writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_RANDOM_EN,
649 +                      nfc->regs + NFC_REG_ECC_CTL);
650 +
651                 offset += ecc->bytes + ecc->prepad;
652                 oob += ecc->bytes + ecc->prepad;
653         }
654 @@ -812,9 +1200,11 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
655                 cnt = mtd->oobsize - (oob - chip->oob_poi);
656                 if (cnt > 0) {
657                         chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
658 -                       chip->write_buf(mtd, oob, cnt);
659 +                       nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE);
660 +                       nand_rnd_write_buf(mtd, oob, cnt);
661                 }
662         }
663 +       nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
664  
665         tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
666         tmp &= ~NFC_ECC_EN;
667 @@ -824,6 +1214,128 @@ static int sunxi_nfc_hw_syndrome_ecc_wri
668         return 0;
669  }
670  
671 +static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state,
672 +                                              int column, int *left)
673 +{
674 +       struct nand_chip *chip = mtd->priv;
675 +       struct nand_ecc_ctrl *ecc = chip->cur_ecc;
676 +       struct sunxi_nand_hw_rnd *rnd = chip->cur_rnd->priv;
677 +       int eccsteps = mtd->writesize / ecc->size;
678 +       int modsize = ecc->size + ecc->prepad + ecc->bytes;
679 +       int steps;
680 +
681 +       if (column < (eccsteps * modsize)) {
682 +               steps = column % modsize;
683 +               *left = modsize - steps;
684 +               if (steps >= ecc->size) {
685 +                       steps -= ecc->size;
686 +                       state = rnd->subseeds[rnd->page % rnd->nseeds];
687 +               }
688 +       } else {
689 +               steps = column % 4096;
690 +               *left = mtd->writesize + mtd->oobsize - column;
691 +       }
692 +
693 +       return sunxi_nfc_hwrnd_step(rnd, state, steps);
694 +}
695 +
696 +static u16 default_seeds[] = {0x4a80};
697 +
698 +static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd)
699 +{
700 +       struct sunxi_nand_hw_rnd *hwrnd = rnd->priv;
701 +
702 +       if (hwrnd->seeds != default_seeds)
703 +               kfree(hwrnd->seeds);
704 +       kfree(hwrnd->subseeds);
705 +       kfree(rnd->layout);
706 +       kfree(hwrnd);
707 +}
708 +
709 +static int sunxi_nand_rnd_ctrl_init(struct mtd_info *mtd,
710 +                                   struct nand_rnd_ctrl *rnd,
711 +                                   struct nand_ecc_ctrl *ecc,
712 +                                   struct device_node *np)
713 +{
714 +       struct sunxi_nand_hw_rnd *hwrnd;
715 +       struct nand_rnd_layout *layout = NULL;
716 +       int ret;
717 +
718 +       hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL);
719 +       if (!hwrnd)
720 +               return -ENOMEM;
721 +
722 +       hwrnd->seeds = default_seeds;
723 +       hwrnd->nseeds = ARRAY_SIZE(default_seeds);
724 +
725 +       if (of_get_property(np, "nand-randomizer-seeds", &ret)) {
726 +               hwrnd->nseeds = ret / sizeof(*hwrnd->seeds);
727 +               hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds),
728 +                                      GFP_KERNEL);
729 +               if (!hwrnd->seeds) {
730 +                       ret = -ENOMEM;
731 +                       goto err;
732 +               }
733 +
734 +               ret = of_property_read_u16_array(np, "nand-randomizer-seeds",
735 +                                                hwrnd->seeds, hwrnd->nseeds);
736 +               if (ret)
737 +                       goto err;
738 +       }
739 +
740 +       switch (ecc->mode) {
741 +       case NAND_ECC_HW_SYNDROME:
742 +               hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps;
743 +               break;
744 +
745 +       case NAND_ECC_HW:
746 +               hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps;
747 +
748 +       default:
749 +               layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree),
750 +                                GFP_KERNEL);
751 +               if (!layout) {
752 +                       ret = -ENOMEM;
753 +                       goto err;
754 +               }
755 +               layout->nranges = 1;
756 +               layout->ranges[0].offset = mtd->writesize;
757 +               layout->ranges[0].length = 2;
758 +               rnd->layout = layout;
759 +               break;
760 +       }
761 +
762 +       if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) {
763 +               int i;
764 +
765 +               hwrnd->subseeds = kzalloc(hwrnd->nseeds *
766 +                                         sizeof(*hwrnd->subseeds),
767 +                                         GFP_KERNEL);
768 +               if (!hwrnd->subseeds) {
769 +                       ret = -ENOMEM;
770 +                       goto err;
771 +               }
772 +
773 +               for (i = 0; i < hwrnd->nseeds; i++)
774 +                       hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd,
775 +                                                       hwrnd->seeds[i],
776 +                                                       ecc->size);
777 +       }
778 +
779 +       rnd->config = sunxi_nfc_hwrnd_config;
780 +       rnd->read_buf = sunxi_nfc_hwrnd_read_buf;
781 +       rnd->write_buf = sunxi_nfc_hwrnd_write_buf;
782 +       rnd->priv = hwrnd;
783 +
784 +       return 0;
785 +
786 +err:
787 +       kfree(hwrnd);
788 +       kfree(layout);
789 +
790 +       return ret;
791 +}
792 +
793  static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
794                                        const struct nand_sdr_timings *timings)
795  {
796 @@ -1084,6 +1596,40 @@ static int sunxi_nand_hw_syndrome_ecc_ct
797         return 0;
798  }
799  
800 +static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd)
801 +{
802 +       switch (rnd->mode) {
803 +       case NAND_RND_HW:
804 +               sunxi_nand_rnd_ctrl_cleanup(rnd);
805 +               break;
806 +       default:
807 +               break;
808 +       }
809 +}
810 +
811 +static int sunxi_nand_rnd_init(struct mtd_info *mtd,
812 +                              struct nand_rnd_ctrl *rnd,
813 +                              struct nand_ecc_ctrl *ecc,
814 +                              struct device_node *np)
815 +{
816 +       int ret;
817 +
818 +       rnd->mode = NAND_RND_NONE;
819 +
820 +       ret = of_get_nand_rnd_mode(np);
821 +       if (ret >= 0)
822 +               rnd->mode = ret;
823 +
824 +       switch (rnd->mode) {
825 +       case NAND_RND_HW:
826 +               return sunxi_nand_rnd_ctrl_init(mtd, rnd, ecc, np);
827 +       default:
828 +               break;
829 +       }
830 +
831 +       return 0;
832 +}
833 +
834  static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
835  {
836         switch (ecc->mode) {
837 @@ -1175,7 +1721,14 @@ struct nand_part *sunxi_ofnandpart_parse
838         if (ret)
839                 goto err;
840  
841 +       ret = sunxi_nand_rnd_init(master, &part->rnd, &part->ecc, pp);
842 +       if (ret) {
843 +               sunxi_nand_ecc_cleanup(&part->ecc);
844 +               goto err;
845 +       }
846 +
847         part->part.ecc = &part->ecc;
848 +       part->part.rnd = &part->rnd;
849  
850         return &part->part;
851  
852 @@ -1300,18 +1853,30 @@ static int sunxi_nand_chip_init(struct d
853         if (ret)
854                 return ret;
855  
856 +       chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
857 +       if (!chip->buffer)
858 +               return -ENOMEM;
859 +
860         ret = sunxi_nand_chip_init_timings(chip, np);
861         if (ret) {
862                 dev_err(dev, "could not configure chip timings: %d\n", ret);
863                 return ret;
864         }
865  
866 +       ret = nand_pst_create(mtd);
867 +       if (ret)
868 +               return ret;
869 +
870         ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
871         if (ret) {
872                 dev_err(dev, "ECC init failed: %d\n", ret);
873                 return ret;
874         }
875  
876 +       ret = sunxi_nand_rnd_init(mtd, &nand->rnd, &nand->ecc, np);
877 +       if (ret)
878 +               return ret;
879 +
880         ret = nand_scan_tail(mtd);
881         if (ret) {
882                 dev_err(dev, "nand_scan_tail failed: %d\n", ret);
883 @@ -1367,6 +1932,8 @@ static void sunxi_nand_chips_cleanup(str
884                                         node);
885                 nand_release(&chip->mtd);
886                 sunxi_nand_ecc_cleanup(&chip->nand.ecc);
887 +               sunxi_nand_rnd_cleanup(&chip->nand.rnd);
888 +               kfree(chip->buffer);
889         }
890  }
891