aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorBoris BREZILLON <boris.brezillon@free-electrons.com>2014-11-30 13:10:28 -0500
committerBrian Norris <computersforpeace@gmail.com>2014-12-01 03:41:49 -0500
commit66de54a761831f2e9f31941fa49c94bfcde5f586 (patch)
tree8bb2900334d145e0cd02ded11e34296a4867fd75 /drivers/mtd
parent05241aead9b98c28476f74e7e1c5ce480ef40ac1 (diff)
mtd: nand: gpmi: add gpmi_copy_bits function
Add a new function to copy bits (not bytes) from a memory region to another one. This function is similar to memcpy except it acts at bit level. It is needed to implement GPMI raw access functions and adapt to the hardware ECC engine which does not pad ECC bits to the next byte boundary. Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Tested-by: Huang Shijie <shijie8@gmail.com> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'drivers/mtd')
-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