uniphier_nand.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /*
  2. * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <assert.h>
  7. #include <stdint.h>
  8. #include <platform_def.h>
  9. #include <arch_helpers.h>
  10. #include <common/debug.h>
  11. #include <drivers/io/io_block.h>
  12. #include <lib/mmio.h>
  13. #include <lib/utils_def.h>
  14. #include "uniphier.h"
  15. #define NAND_CMD_READ0 0
  16. #define NAND_CMD_READSTART 0x30
  17. #define DENALI_ECC_ENABLE 0x0e0
  18. #define DENALI_PAGES_PER_BLOCK 0x150
  19. #define DENALI_DEVICE_MAIN_AREA_SIZE 0x170
  20. #define DENALI_DEVICE_SPARE_AREA_SIZE 0x180
  21. #define DENALI_TWO_ROW_ADDR_CYCLES 0x190
  22. #define DENALI_INTR_STATUS0 0x410
  23. #define DENALI_INTR_ECC_UNCOR_ERR BIT(1)
  24. #define DENALI_INTR_DMA_CMD_COMP BIT(2)
  25. #define DENALI_INTR_INT_ACT BIT(12)
  26. #define DENALI_DMA_ENABLE 0x700
  27. #define DENALI_HOST_ADDR 0x00
  28. #define DENALI_HOST_DATA 0x10
  29. #define DENALI_MAP01 (1 << 26)
  30. #define DENALI_MAP10 (2 << 26)
  31. #define DENALI_MAP11 (3 << 26)
  32. #define DENALI_MAP11_CMD ((DENALI_MAP11) | 0)
  33. #define DENALI_MAP11_ADDR ((DENALI_MAP11) | 1)
  34. #define DENALI_MAP11_DATA ((DENALI_MAP11) | 2)
  35. #define DENALI_ACCESS_DEFAULT_AREA 0x42
  36. #define UNIPHIER_NAND_BBT_UNKNOWN 0xff
  37. struct uniphier_nand {
  38. uintptr_t host_base;
  39. uintptr_t reg_base;
  40. int pages_per_block;
  41. int page_size;
  42. int two_row_addr_cycles;
  43. uint8_t bbt[16];
  44. };
  45. struct uniphier_nand uniphier_nand;
  46. static void uniphier_nand_host_write(struct uniphier_nand *nand,
  47. uint32_t addr, uint32_t data)
  48. {
  49. mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr);
  50. mmio_write_32(nand->host_base + DENALI_HOST_DATA, data);
  51. }
  52. static uint32_t uniphier_nand_host_read(struct uniphier_nand *nand,
  53. uint32_t addr)
  54. {
  55. mmio_write_32(nand->host_base + DENALI_HOST_ADDR, addr);
  56. return mmio_read_32(nand->host_base + DENALI_HOST_DATA);
  57. }
  58. static int uniphier_nand_block_isbad(struct uniphier_nand *nand, int block)
  59. {
  60. int page = nand->pages_per_block * block;
  61. int column = nand->page_size;
  62. uint8_t bbm;
  63. uint32_t status;
  64. int is_bad;
  65. /* use cache if available */
  66. if (block < ARRAY_SIZE(nand->bbt) &&
  67. nand->bbt[block] != UNIPHIER_NAND_BBT_UNKNOWN)
  68. return nand->bbt[block];
  69. mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 0);
  70. mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1);
  71. uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READ0);
  72. uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, column & 0xff);
  73. uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (column >> 8) & 0xff);
  74. uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, page & 0xff);
  75. uniphier_nand_host_write(nand, DENALI_MAP11_ADDR, (page >> 8) & 0xff);
  76. if (!nand->two_row_addr_cycles)
  77. uniphier_nand_host_write(nand, DENALI_MAP11_ADDR,
  78. (page >> 16) & 0xff);
  79. uniphier_nand_host_write(nand, DENALI_MAP11_CMD, NAND_CMD_READSTART);
  80. do {
  81. status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0);
  82. } while (!(status & DENALI_INTR_INT_ACT));
  83. bbm = uniphier_nand_host_read(nand, DENALI_MAP11_DATA);
  84. is_bad = bbm != 0xff;
  85. /* if possible, save the result for future re-use */
  86. if (block < ARRAY_SIZE(nand->bbt))
  87. nand->bbt[block] = is_bad;
  88. if (is_bad)
  89. WARN("found bad block at %d. skip.\n", block);
  90. return is_bad;
  91. }
  92. static int uniphier_nand_read_pages(struct uniphier_nand *nand, uintptr_t buf,
  93. int page_start, int page_count)
  94. {
  95. uint32_t status;
  96. mmio_write_32(nand->reg_base + DENALI_ECC_ENABLE, 1);
  97. mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 1);
  98. mmio_write_32(nand->reg_base + DENALI_INTR_STATUS0, -1);
  99. /* use Data DMA (64bit) */
  100. mmio_write_32(nand->host_base + DENALI_HOST_ADDR,
  101. DENALI_MAP10 | page_start);
  102. /*
  103. * 1. setup transfer type, interrupt when complete,
  104. * burst len = 64 bytes, the number of pages
  105. */
  106. mmio_write_32(nand->host_base + DENALI_HOST_DATA,
  107. 0x01002000 | (64 << 16) | page_count);
  108. /* 2. set memory low address */
  109. mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf);
  110. /* 3. set memory high address */
  111. mmio_write_32(nand->host_base + DENALI_HOST_DATA, buf >> 32);
  112. do {
  113. status = mmio_read_32(nand->reg_base + DENALI_INTR_STATUS0);
  114. } while (!(status & DENALI_INTR_DMA_CMD_COMP));
  115. mmio_write_32(nand->reg_base + DENALI_DMA_ENABLE, 0);
  116. if (status & DENALI_INTR_ECC_UNCOR_ERR) {
  117. ERROR("uncorrectable error in page range %d-%d",
  118. page_start, page_start + page_count - 1);
  119. return -EBADMSG;
  120. }
  121. return 0;
  122. }
  123. static size_t __uniphier_nand_read(struct uniphier_nand *nand, int lba,
  124. uintptr_t buf, size_t size)
  125. {
  126. int pages_per_block = nand->pages_per_block;
  127. int page_size = nand->page_size;
  128. int blocks_to_skip = lba / pages_per_block;
  129. int pages_to_read = div_round_up(size, page_size);
  130. int page = lba % pages_per_block;
  131. int block = 0;
  132. uintptr_t p = buf;
  133. int page_count, ret;
  134. while (blocks_to_skip) {
  135. ret = uniphier_nand_block_isbad(nand, block);
  136. if (ret < 0)
  137. goto out;
  138. if (!ret)
  139. blocks_to_skip--;
  140. block++;
  141. }
  142. while (pages_to_read) {
  143. ret = uniphier_nand_block_isbad(nand, block);
  144. if (ret < 0)
  145. goto out;
  146. if (ret) {
  147. block++;
  148. continue;
  149. }
  150. page_count = MIN(pages_per_block - page, pages_to_read);
  151. ret = uniphier_nand_read_pages(nand, p,
  152. block * pages_per_block + page,
  153. page_count);
  154. if (ret)
  155. goto out;
  156. block++;
  157. page = 0;
  158. p += page_size * page_count;
  159. pages_to_read -= page_count;
  160. }
  161. out:
  162. /* number of read bytes */
  163. return MIN(size, p - buf);
  164. }
  165. static size_t uniphier_nand_read(int lba, uintptr_t buf, size_t size)
  166. {
  167. size_t count;
  168. inv_dcache_range(buf, size);
  169. count = __uniphier_nand_read(&uniphier_nand, lba, buf, size);
  170. inv_dcache_range(buf, size);
  171. return count;
  172. }
  173. static struct io_block_dev_spec uniphier_nand_dev_spec = {
  174. .ops = {
  175. .read = uniphier_nand_read,
  176. },
  177. /* fill .block_size at run-time */
  178. };
  179. static int uniphier_nand_hw_init(struct uniphier_nand *nand)
  180. {
  181. int i;
  182. for (i = 0; i < ARRAY_SIZE(nand->bbt); i++)
  183. nand->bbt[i] = UNIPHIER_NAND_BBT_UNKNOWN;
  184. nand->reg_base = nand->host_base + 0x100000;
  185. nand->pages_per_block =
  186. mmio_read_32(nand->reg_base + DENALI_PAGES_PER_BLOCK);
  187. nand->page_size =
  188. mmio_read_32(nand->reg_base + DENALI_DEVICE_MAIN_AREA_SIZE);
  189. if (mmio_read_32(nand->reg_base + DENALI_TWO_ROW_ADDR_CYCLES) & BIT(0))
  190. nand->two_row_addr_cycles = 1;
  191. uniphier_nand_host_write(nand, DENALI_MAP10,
  192. DENALI_ACCESS_DEFAULT_AREA);
  193. return 0;
  194. }
  195. static const uintptr_t uniphier_nand_base[] = {
  196. [UNIPHIER_SOC_LD11] = 0x68000000,
  197. [UNIPHIER_SOC_LD20] = 0x68000000,
  198. [UNIPHIER_SOC_PXS3] = 0x68000000,
  199. };
  200. int uniphier_nand_init(unsigned int soc,
  201. struct io_block_dev_spec **block_dev_spec)
  202. {
  203. int ret;
  204. assert(soc < ARRAY_SIZE(uniphier_nand_base));
  205. uniphier_nand.host_base = uniphier_nand_base[soc];
  206. if (!uniphier_nand.host_base)
  207. return -ENOTSUP;
  208. ret = uniphier_nand_hw_init(&uniphier_nand);
  209. if (ret)
  210. return ret;
  211. uniphier_nand_dev_spec.block_size = uniphier_nand.page_size;
  212. *block_dev_spec = &uniphier_nand_dev_spec;
  213. return 0;
  214. }