blob: 02b9d06f6743eb96e3bc8bd1ddd69e56b1bb310d [file] [log] [blame]
/*
* 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;
}