atmel.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2005, Intec Automation Inc.
  4. * Copyright (C) 2014, Freescale Semiconductor, Inc.
  5. */
  6. #include <linux/mtd/spi-nor.h>
  7. #include "core.h"
  8. #define ATMEL_SR_GLOBAL_PROTECT_MASK GENMASK(5, 2)
  9. /*
  10. * The Atmel AT25FS010/AT25FS040 parts have some weird configuration for the
  11. * block protection bits. We don't support them. But legacy behavior in linux
  12. * is to unlock the whole flash array on startup. Therefore, we have to support
  13. * exactly this operation.
  14. */
  15. static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
  16. {
  17. return -EOPNOTSUPP;
  18. }
  19. static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
  20. {
  21. int ret;
  22. /* We only support unlocking the whole flash array */
  23. if (ofs || len != nor->params->size)
  24. return -EINVAL;
  25. /* Write 0x00 to the status register to disable write protection */
  26. ret = spi_nor_write_sr_and_check(nor, 0);
  27. if (ret)
  28. dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n");
  29. return ret;
  30. }
  31. static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
  32. {
  33. return -EOPNOTSUPP;
  34. }
  35. static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
  36. .lock = at25fs_nor_lock,
  37. .unlock = at25fs_nor_unlock,
  38. .is_locked = at25fs_nor_is_locked,
  39. };
  40. static void at25fs_nor_late_init(struct spi_nor *nor)
  41. {
  42. nor->params->locking_ops = &at25fs_nor_locking_ops;
  43. }
  44. static const struct spi_nor_fixups at25fs_nor_fixups = {
  45. .late_init = at25fs_nor_late_init,
  46. };
  47. /**
  48. * atmel_nor_set_global_protection - Do a Global Protect or Unprotect command
  49. * @nor: pointer to 'struct spi_nor'
  50. * @ofs: offset in bytes
  51. * @len: len in bytes
  52. * @is_protect: if true do a Global Protect otherwise it is a Global Unprotect
  53. *
  54. * Return: 0 on success, -error otherwise.
  55. */
  56. static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
  57. uint64_t len, bool is_protect)
  58. {
  59. int ret;
  60. u8 sr;
  61. /* We only support locking the whole flash array */
  62. if (ofs || len != nor->params->size)
  63. return -EINVAL;
  64. ret = spi_nor_read_sr(nor, nor->bouncebuf);
  65. if (ret)
  66. return ret;
  67. sr = nor->bouncebuf[0];
  68. /* SRWD bit needs to be cleared, otherwise the protection doesn't change */
  69. if (sr & SR_SRWD) {
  70. sr &= ~SR_SRWD;
  71. ret = spi_nor_write_sr_and_check(nor, sr);
  72. if (ret) {
  73. dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
  74. return ret;
  75. }
  76. }
  77. if (is_protect) {
  78. sr |= ATMEL_SR_GLOBAL_PROTECT_MASK;
  79. /*
  80. * Set the SRWD bit again as soon as we are protecting
  81. * anything. This will ensure that the WP# pin is working
  82. * correctly. By doing this we also behave the same as
  83. * spi_nor_sr_lock(), which sets SRWD if any block protection
  84. * is active.
  85. */
  86. sr |= SR_SRWD;
  87. } else {
  88. sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK;
  89. }
  90. nor->bouncebuf[0] = sr;
  91. /*
  92. * We cannot use the spi_nor_write_sr_and_check() because this command
  93. * isn't really setting any bits, instead it is an pseudo command for
  94. * "Global Unprotect" or "Global Protect"
  95. */
  96. return spi_nor_write_sr(nor, nor->bouncebuf, 1);
  97. }
  98. static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs,
  99. uint64_t len)
  100. {
  101. return atmel_nor_set_global_protection(nor, ofs, len, true);
  102. }
  103. static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs,
  104. uint64_t len)
  105. {
  106. return atmel_nor_set_global_protection(nor, ofs, len, false);
  107. }
  108. static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
  109. uint64_t len)
  110. {
  111. int ret;
  112. if (ofs >= nor->params->size || (ofs + len) > nor->params->size)
  113. return -EINVAL;
  114. ret = spi_nor_read_sr(nor, nor->bouncebuf);
  115. if (ret)
  116. return ret;
  117. return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
  118. }
  119. static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
  120. .lock = atmel_nor_global_protect,
  121. .unlock = atmel_nor_global_unprotect,
  122. .is_locked = atmel_nor_is_global_protected,
  123. };
  124. static void atmel_nor_global_protection_late_init(struct spi_nor *nor)
  125. {
  126. nor->params->locking_ops = &atmel_nor_global_protection_ops;
  127. }
  128. static const struct spi_nor_fixups atmel_nor_global_protection_fixups = {
  129. .late_init = atmel_nor_global_protection_late_init,
  130. };
  131. static const struct flash_info atmel_nor_parts[] = {
  132. /* Atmel -- some are (confusingly) marketed as "DataFlash" */
  133. { "at25fs010", INFO(0x1f6601, 0, 32 * 1024, 4)
  134. FLAGS(SPI_NOR_HAS_LOCK)
  135. NO_SFDP_FLAGS(SECT_4K)
  136. .fixups = &at25fs_nor_fixups },
  137. { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8)
  138. FLAGS(SPI_NOR_HAS_LOCK)
  139. NO_SFDP_FLAGS(SECT_4K)
  140. .fixups = &at25fs_nor_fixups },
  141. { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8)
  142. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  143. NO_SFDP_FLAGS(SECT_4K)
  144. .fixups = &atmel_nor_global_protection_fixups },
  145. { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64)
  146. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  147. NO_SFDP_FLAGS(SECT_4K)
  148. .fixups = &atmel_nor_global_protection_fixups },
  149. { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64)
  150. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  151. NO_SFDP_FLAGS(SECT_4K)
  152. .fixups = &atmel_nor_global_protection_fixups },
  153. { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128)
  154. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  155. NO_SFDP_FLAGS(SECT_4K)
  156. .fixups = &atmel_nor_global_protection_fixups },
  157. { "at25sl321", INFO(0x1f4216, 0, 64 * 1024, 64)
  158. NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
  159. { "at26f004", INFO(0x1f0400, 0, 64 * 1024, 8)
  160. NO_SFDP_FLAGS(SECT_4K) },
  161. { "at26df081a", INFO(0x1f4501, 0, 64 * 1024, 16)
  162. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  163. NO_SFDP_FLAGS(SECT_4K)
  164. .fixups = &atmel_nor_global_protection_fixups },
  165. { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32)
  166. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  167. NO_SFDP_FLAGS(SECT_4K)
  168. .fixups = &atmel_nor_global_protection_fixups },
  169. { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64)
  170. FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
  171. NO_SFDP_FLAGS(SECT_4K)
  172. .fixups = &atmel_nor_global_protection_fixups },
  173. { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16)
  174. NO_SFDP_FLAGS(SECT_4K) },
  175. };
  176. const struct spi_nor_manufacturer spi_nor_atmel = {
  177. .name = "atmel",
  178. .parts = atmel_nor_parts,
  179. .nparts = ARRAY_SIZE(atmel_nor_parts),
  180. };