aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-lib.c153
-rw-r--r--drivers/mtd/nand/gpmi-nand/gpmi-nand.h4
2 files changed, 157 insertions, 0 deletions
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
index 87e658ce23ef..27f272ed502a 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
@@ -1353,3 +1353,156 @@ int gpmi_read_page(struct gpmi_nand_data *this,
1353 set_dma_type(this, DMA_FOR_READ_ECC_PAGE); 1353 set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
1354 return start_dma_with_bch_irq(this, desc); 1354 return start_dma_with_bch_irq(this, desc);
1355} 1355}
1356
1357/**
1358 * gpmi_copy_bits - copy bits from one memory region to another
1359 * @dst: destination buffer
1360 * @dst_bit_off: bit offset we're starting to write at
1361 * @src: source buffer
1362 * @src_bit_off: bit offset we're starting to read from
1363 * @nbits: number of bits to copy
1364 *
1365 * This functions copies bits from one memory region to another, and is used by
1366 * the GPMI driver to copy ECC sections which are not guaranteed to be byte
1367 * aligned.
1368 *
1369 * src and dst should not overlap.
1370 *
1371 */
1372void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
1373 const u8 *src, size_t src_bit_off,
1374 size_t nbits)
1375{
1376 size_t i;
1377 size_t nbytes;
1378 u32 src_buffer = 0;
1379 size_t bits_in_src_buffer = 0;
1380
1381 if (!nbits)
1382 return;
1383
1384 /*
1385 * Move src and dst pointers to the closest byte pointer and store bit
1386 * offsets within a byte.
1387 */
1388 src += src_bit_off / 8;
1389 src_bit_off %= 8;
1390
1391 dst += dst_bit_off / 8;
1392 dst_bit_off %= 8;
1393
1394 /*
1395 * Initialize the src_buffer value with bits available in the first
1396 * byte of data so that we end up with a byte aligned src pointer.
1397 */
1398 if (src_bit_off) {
1399 src_buffer = src[0] >> src_bit_off;
1400 if (nbits >= (8 - src_bit_off)) {
1401 bits_in_src_buffer += 8 - src_bit_off;
1402 } else {
1403 src_buffer &= GENMASK(nbits - 1, 0);
1404 bits_in_src_buffer += nbits;
1405 }
1406 nbits -= bits_in_src_buffer;
1407 src++;
1408 }
1409
1410 /* Calculate the number of bytes that can be copied from src to dst. */
1411 nbytes = nbits / 8;
1412
1413 /* Try to align dst to a byte boundary. */
1414 if (dst_bit_off) {
1415 if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) {
1416 src_buffer |= src[0] << bits_in_src_buffer;
1417 bits_in_src_buffer += 8;
1418 src++;
1419 nbytes--;
1420 }
1421
1422 if (bits_in_src_buffer >= (8 - dst_bit_off)) {
1423 dst[0] &= GENMASK(dst_bit_off - 1, 0);
1424 dst[0] |= src_buffer << dst_bit_off;
1425 src_buffer >>= (8 - dst_bit_off);
1426 bits_in_src_buffer -= (8 - dst_bit_off);
1427 dst_bit_off = 0;
1428 dst++;
1429 if (bits_in_src_buffer > 7) {
1430 bits_in_src_buffer -= 8;
1431 dst[0] = src_buffer;
1432 dst++;
1433 src_buffer >>= 8;
1434 }
1435 }
1436 }
1437
1438 if (!bits_in_src_buffer && !dst_bit_off) {
1439 /*
1440 * Both src and dst pointers are byte aligned, thus we can
1441 * just use the optimized memcpy function.
1442 */
1443 if (nbytes)
1444 memcpy(dst, src, nbytes);
1445 } else {
1446 /*
1447 * src buffer is not byte aligned, hence we have to copy each
1448 * src byte to the src_buffer variable before extracting a byte
1449 * to store in dst.
1450 */
1451 for (i = 0; i < nbytes; i++) {
1452 src_buffer |= src[i] << bits_in_src_buffer;
1453 dst[i] = src_buffer;
1454 src_buffer >>= 8;
1455 }
1456 }
1457 /* Update dst and src pointers */
1458 dst += nbytes;
1459 src += nbytes;
1460
1461 /*
1462 * nbits is the number of remaining bits. It should not exceed 8 as
1463 * we've already copied as much bytes as possible.
1464 */
1465 nbits %= 8;
1466
1467 /*
1468 * If there's no more bits to copy to the destination and src buffer
1469 * was already byte aligned, then we're done.
1470 */
1471 if (!nbits && !bits_in_src_buffer)
1472 return;
1473
1474 /* Copy the remaining bits to src_buffer */
1475 if (nbits)
1476 src_buffer |= (*src & GENMASK(nbits - 1, 0)) <<
1477 bits_in_src_buffer;
1478 bits_in_src_buffer += nbits;
1479
1480 /*
1481 * In case there were not enough bits to get a byte aligned dst buffer
1482 * prepare the src_buffer variable to match the dst organization (shift
1483 * src_buffer by dst_bit_off and retrieve the least significant bits
1484 * from dst).
1485 */
1486 if (dst_bit_off)
1487 src_buffer = (src_buffer << dst_bit_off) |
1488 (*dst & GENMASK(dst_bit_off - 1, 0));
1489 bits_in_src_buffer += dst_bit_off;
1490
1491 /*
1492 * Keep most significant bits from dst if we end up with an unaligned
1493 * number of bits.
1494 */
1495 nbytes = bits_in_src_buffer / 8;
1496 if (bits_in_src_buffer % 8) {
1497 src_buffer |= (dst[nbytes] &
1498 GENMASK(7, bits_in_src_buffer % 8)) <<
1499 (nbytes * 8);
1500 nbytes++;
1501 }
1502
1503 /* Copy the remaining bytes to dst */
1504 for (i = 0; i < nbytes; i++) {
1505 dst[i] = src_buffer;
1506 src_buffer >>= 8;
1507 }
1508}
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
index 32c6ba49f986..20da1f139853 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.h
@@ -290,6 +290,10 @@ extern int gpmi_send_page(struct gpmi_nand_data *,
290extern int gpmi_read_page(struct gpmi_nand_data *, 290extern int gpmi_read_page(struct gpmi_nand_data *,
291 dma_addr_t payload, dma_addr_t auxiliary); 291 dma_addr_t payload, dma_addr_t auxiliary);
292 292
293void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
294 const u8 *src, size_t src_bit_off,
295 size_t nbits);
296
293/* BCH : Status Block Completion Codes */ 297/* BCH : Status Block Completion Codes */
294#define STATUS_GOOD 0x00 298#define STATUS_GOOD 0x00
295#define STATUS_ERASED 0xff 299#define STATUS_ERASED 0xff