diff options
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 153 | ||||
-rw-r--r-- | drivers/mtd/nand/gpmi-nand/gpmi-nand.h | 4 |
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 | */ | ||
1372 | void 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 *, | |||
290 | extern int gpmi_read_page(struct gpmi_nand_data *, | 290 | extern 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 | ||
293 | void 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 |