alliancememory.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Author: Mario Kicherer <dev@kicherer.org>
  4. */
  5. #include <linux/device.h>
  6. #include <linux/kernel.h>
  7. #include <linux/mtd/spinand.h>
  8. #define SPINAND_MFR_ALLIANCEMEMORY 0x52
  9. #define AM_STATUS_ECC_BITMASK (3 << 4)
  10. #define AM_STATUS_ECC_NONE_DETECTED (0 << 4)
  11. #define AM_STATUS_ECC_CORRECTED (1 << 4)
  12. #define AM_STATUS_ECC_ERRORED (2 << 4)
  13. #define AM_STATUS_ECC_MAX_CORRECTED (3 << 4)
  14. static SPINAND_OP_VARIANTS(read_cache_variants,
  15. SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
  16. SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
  17. SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
  18. SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
  19. SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
  20. SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
  21. static SPINAND_OP_VARIANTS(write_cache_variants,
  22. SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
  23. SPINAND_PROG_LOAD(true, 0, NULL, 0));
  24. static SPINAND_OP_VARIANTS(update_cache_variants,
  25. SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
  26. SPINAND_PROG_LOAD(false, 0, NULL, 0));
  27. static int am_get_eccsize(struct mtd_info *mtd)
  28. {
  29. if (mtd->oobsize == 64)
  30. return 0x20;
  31. else if (mtd->oobsize == 128)
  32. return 0x38;
  33. else if (mtd->oobsize == 256)
  34. return 0x70;
  35. else
  36. return -EINVAL;
  37. }
  38. static int am_ooblayout_ecc(struct mtd_info *mtd, int section,
  39. struct mtd_oob_region *region)
  40. {
  41. int ecc_bytes;
  42. ecc_bytes = am_get_eccsize(mtd);
  43. if (ecc_bytes < 0)
  44. return ecc_bytes;
  45. region->offset = mtd->oobsize - ecc_bytes;
  46. region->length = ecc_bytes;
  47. return 0;
  48. }
  49. static int am_ooblayout_free(struct mtd_info *mtd, int section,
  50. struct mtd_oob_region *region)
  51. {
  52. int ecc_bytes;
  53. if (section)
  54. return -ERANGE;
  55. ecc_bytes = am_get_eccsize(mtd);
  56. if (ecc_bytes < 0)
  57. return ecc_bytes;
  58. /*
  59. * It is unclear how many bytes are used for the bad block marker. We
  60. * reserve the common two bytes here.
  61. *
  62. * The free area in this kind of flash is divided into chunks where the
  63. * first 4 bytes of each chunk are unprotected. The number of chunks
  64. * depends on the specific model. The models with 4096+256 bytes pages
  65. * have 8 chunks, the others 4 chunks.
  66. */
  67. region->offset = 2;
  68. region->length = mtd->oobsize - 2 - ecc_bytes;
  69. return 0;
  70. }
  71. static const struct mtd_ooblayout_ops am_ooblayout = {
  72. .ecc = am_ooblayout_ecc,
  73. .free = am_ooblayout_free,
  74. };
  75. static int am_ecc_get_status(struct spinand_device *spinand, u8 status)
  76. {
  77. switch (status & AM_STATUS_ECC_BITMASK) {
  78. case AM_STATUS_ECC_NONE_DETECTED:
  79. return 0;
  80. case AM_STATUS_ECC_CORRECTED:
  81. /*
  82. * use oobsize to determine the flash model and the maximum of
  83. * correctable errors and return maximum - 1 by convention
  84. */
  85. if (spinand->base.mtd.oobsize == 64)
  86. return 3;
  87. else
  88. return 7;
  89. case AM_STATUS_ECC_ERRORED:
  90. return -EBADMSG;
  91. case AM_STATUS_ECC_MAX_CORRECTED:
  92. /*
  93. * use oobsize to determine the flash model and the maximum of
  94. * correctable errors
  95. */
  96. if (spinand->base.mtd.oobsize == 64)
  97. return 4;
  98. else
  99. return 8;
  100. default:
  101. break;
  102. }
  103. return -EINVAL;
  104. }
  105. static const struct spinand_info alliancememory_spinand_table[] = {
  106. SPINAND_INFO("AS5F34G04SND",
  107. SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x2f),
  108. NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
  109. NAND_ECCREQ(4, 512),
  110. SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
  111. &write_cache_variants,
  112. &update_cache_variants),
  113. SPINAND_HAS_QE_BIT,
  114. SPINAND_ECCINFO(&am_ooblayout,
  115. am_ecc_get_status)),
  116. };
  117. static const struct spinand_manufacturer_ops alliancememory_spinand_manuf_ops = {
  118. };
  119. const struct spinand_manufacturer alliancememory_spinand_manufacturer = {
  120. .id = SPINAND_MFR_ALLIANCEMEMORY,
  121. .name = "AllianceMemory",
  122. .chips = alliancememory_spinand_table,
  123. .nchips = ARRAY_SIZE(alliancememory_spinand_table),
  124. .ops = &alliancememory_spinand_manuf_ops,
  125. };