| /* |
| * Randomize the data in NAND. |
| * |
| * Based on the BG2 linux kernel nand randomizer code |
| * by Yongsen Chen <YongsenChen@gmail.com> |
| * |
| * Difference between this rewritten version and previous code: |
| * 1. linux kernel only |
| * 2. simplify the code |
| * 3. performance improvement |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/errno.h> |
| #include <linux/types.h> |
| |
| #include "nand_randomizer.h" |
| #include "prbs.h" |
| |
| #define NAND_RANDOMIZER_SEED (0x576A) |
| |
| static inline bool skip_randomize(struct nand_randomizer *randomizer, |
| int page) |
| { |
| return page < randomizer->page_start; |
| } |
| |
| static int get_randomize_start(struct nand_randomizer *randomizer, |
| unsigned int page) |
| { |
| int page_in_block = page & randomizer->pageinblock_mask; |
| int start, pos = page_in_block * 2; |
| |
| pos &= randomizer->mask; |
| start = (randomizer->data[pos + 1] << 8) + randomizer->data[pos]; |
| |
| /* |
| * make sure it won't exceed data length. |
| * & (~0x3) is to make sure it's 32-bit aligned |
| */ |
| start &= randomizer->mask; |
| start &= ~0x3; |
| |
| return start; |
| } |
| |
| #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS |
| /* xor_32regs_2() is copied from include/asm-generic/xor.h */ |
| static void |
| xor_32regs_2(unsigned long bytes, unsigned long *p1, unsigned long *p2) |
| { |
| long lines = bytes / (sizeof (long)) / 8; |
| |
| do { |
| register long d0, d1, d2, d3, d4, d5, d6, d7; |
| d0 = p1[0]; /* Pull the stuff into registers */ |
| d1 = p1[1]; /* ... in bursts, if possible. */ |
| d2 = p1[2]; |
| d3 = p1[3]; |
| d4 = p1[4]; |
| d5 = p1[5]; |
| d6 = p1[6]; |
| d7 = p1[7]; |
| d0 ^= p2[0]; |
| d1 ^= p2[1]; |
| d2 ^= p2[2]; |
| d3 ^= p2[3]; |
| d4 ^= p2[4]; |
| d5 ^= p2[5]; |
| d6 ^= p2[6]; |
| d7 ^= p2[7]; |
| p1[0] = d0; /* Store the result (in bursts) */ |
| p1[1] = d1; |
| p1[2] = d2; |
| p1[3] = d3; |
| p1[4] = d4; |
| p1[5] = d5; |
| p1[6] = d6; |
| p1[7] = d7; |
| p1 += 8; |
| p2 += 8; |
| } while (--lines > 0); |
| } |
| |
| static void randomize_by_xor(const uint8_t *dat, uint8_t *src, int count) |
| { |
| int bytes = count / (sizeof(long) * 8); |
| int rem = count % (sizeof(long) * 8); |
| |
| bytes *= sizeof(long) * 8; |
| count = rem / sizeof(long); |
| rem = rem % sizeof(long); |
| |
| if (bytes) { |
| xor_32regs_2(bytes, (unsigned long *)src, (unsigned long *)dat); |
| src += bytes; |
| dat += bytes; |
| } |
| |
| while (count) { |
| *(unsigned long *)src = *(unsigned long *)src ^ *(unsigned long*)dat; |
| src += sizeof(long); |
| dat += sizeof(long); |
| count--; |
| } |
| |
| while (rem) { |
| *src = *src ^ *dat; |
| src++; |
| dat++; |
| rem--; |
| } |
| } |
| #else |
| static void randomize_by_xor(const uint8_t *dat, uint8_t *src, int count) |
| { |
| int i; |
| |
| if (IS_ALIGNED((uint64_t)src, 4) && |
| IS_ALIGNED((uint64_t)dat, 4) && |
| IS_ALIGNED(count, 4)) { |
| |
| count /= 4; |
| while (count) { |
| *(uint32_t *)src = *(uint32_t *)src ^ *(uint32_t *)dat; |
| src += 4; |
| dat += 4; |
| count--; |
| } |
| } else { |
| for (i = 0; i < count; i++) |
| src[i] = src[i] ^ dat[i]; |
| } |
| } |
| #endif |
| |
| static void randomize_by_xor_ring(const uint8_t *random_data, |
| unsigned int random_length, |
| uint8_t *src, |
| unsigned int length, unsigned int start) |
| { |
| unsigned int randomized_length = 0; |
| unsigned int offset = start; |
| |
| /* make sure offset is in a page */ |
| offset = offset % random_length; |
| |
| while (randomized_length < length) { |
| unsigned int processing_length = random_length - offset; |
| |
| if (processing_length > length - randomized_length) |
| processing_length = length - randomized_length; |
| |
| randomize_by_xor(random_data + offset, |
| src + randomized_length, |
| processing_length); |
| offset = 0; |
| randomized_length += processing_length; |
| } |
| } |
| |
| void nand_randomize_page(struct nand_randomizer *randomizer, |
| uint8_t *dat, uint8_t *oob, int page) |
| { |
| int start; |
| |
| if (skip_randomize(randomizer, page)) |
| return; |
| |
| start = get_randomize_start(randomizer, page); |
| |
| if (dat) |
| randomize_by_xor_ring(randomizer->data, randomizer->length, |
| dat, randomizer->writesize, |
| start); |
| |
| if (oob) { |
| uint8_t bbm = oob[0]; |
| randomize_by_xor_ring(randomizer->data, randomizer->length, |
| oob, randomizer->oobsize, |
| start + randomizer->writesize); |
| /* we don't randomize the bad block marker */ |
| oob[0] = bbm; |
| } |
| } |
| |
| int nand_randomize_init(struct nand_randomizer *randomizer, uint32_t erasesize, |
| uint32_t writesize, uint32_t oobsize, |
| uint8_t *data, unsigned int length, |
| int page_start) |
| { |
| int pages_per_block; |
| |
| if (!data) |
| return -EINVAL; |
| |
| randomizer->page_start = page_start; |
| randomizer->data = data; |
| randomizer->length = length; |
| randomizer->mask = length - 1; |
| randomizer->erasesize = erasesize; |
| randomizer->writesize = writesize; |
| randomizer->oobsize = oobsize; |
| pages_per_block = erasesize / writesize; |
| randomizer->pageinblock_mask = pages_per_block - 1; |
| |
| prbs15_gen(PRBS_POLYNOMIAL_DEFAULT, NAND_RANDOMIZER_SEED, |
| data, length, false); |
| |
| pr_info("nand randomizer init done.\n"); |
| |
| return 0; |
| } |