winbond.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  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 WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
  9. #define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */
  10. #define WINBOND_NOR_OP_SELDIE 0xc2 /* Select active die */
  11. #define WINBOND_NOR_WREAR_OP(buf) \
  12. SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \
  13. SPI_MEM_OP_NO_ADDR, \
  14. SPI_MEM_OP_NO_DUMMY, \
  15. SPI_MEM_OP_DATA_OUT(1, buf, 0))
  16. #define WINBOND_NOR_SELDIE_OP(buf) \
  17. SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_SELDIE, 0), \
  18. SPI_MEM_OP_NO_ADDR, \
  19. SPI_MEM_OP_NO_DUMMY, \
  20. SPI_MEM_OP_DATA_OUT(1, buf, 0))
  21. static int
  22. w25q128_post_bfpt_fixups(struct spi_nor *nor,
  23. const struct sfdp_parameter_header *bfpt_header,
  24. const struct sfdp_bfpt *bfpt)
  25. {
  26. /*
  27. * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded
  28. * size is really wrong. It seems that they confused Mbit with MiB.
  29. * Thus the flash is discovered as a 2MiB device.
  30. */
  31. if (bfpt_header->major == SFDP_JESD216_MAJOR &&
  32. bfpt_header->minor == SFDP_JESD216_MINOR &&
  33. nor->params->size == SZ_2M &&
  34. nor->params->erase_map.regions[0].size == SZ_2M) {
  35. nor->params->size = SZ_16M;
  36. nor->params->erase_map.regions[0].size = SZ_16M;
  37. }
  38. return 0;
  39. }
  40. static const struct spi_nor_fixups w25q128_fixups = {
  41. .post_bfpt = w25q128_post_bfpt_fixups,
  42. };
  43. static int
  44. w25q256_post_bfpt_fixups(struct spi_nor *nor,
  45. const struct sfdp_parameter_header *bfpt_header,
  46. const struct sfdp_bfpt *bfpt)
  47. {
  48. /*
  49. * W25Q256JV supports 4B opcodes but W25Q256FV does not.
  50. * Unfortunately, Winbond has re-used the same JEDEC ID for both
  51. * variants which prevents us from defining a new entry in the parts
  52. * table.
  53. * To differentiate between W25Q256JV and W25Q256FV check SFDP header
  54. * version: only JV has JESD216A compliant structure (version 5).
  55. */
  56. if (bfpt_header->major == SFDP_JESD216_MAJOR &&
  57. bfpt_header->minor == SFDP_JESD216A_MINOR)
  58. nor->flags |= SNOR_F_4B_OPCODES;
  59. return 0;
  60. }
  61. static const struct spi_nor_fixups w25q256_fixups = {
  62. .post_bfpt = w25q256_post_bfpt_fixups,
  63. };
  64. /**
  65. * winbond_nor_select_die() - Set active die.
  66. * @nor: pointer to 'struct spi_nor'.
  67. * @die: die to set active.
  68. *
  69. * Certain Winbond chips feature more than a single die. This is mostly hidden
  70. * to the user, except that some chips may experience time deviation when
  71. * modifying the status bits between dies, which in some corner cases may
  72. * produce problematic races. Being able to explicitly select a die to check its
  73. * state in this case may be useful.
  74. *
  75. * Return: 0 on success, -errno otherwise.
  76. */
  77. static int winbond_nor_select_die(struct spi_nor *nor, u8 die)
  78. {
  79. int ret;
  80. nor->bouncebuf[0] = die;
  81. if (nor->spimem) {
  82. struct spi_mem_op op = WINBOND_NOR_SELDIE_OP(nor->bouncebuf);
  83. spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
  84. ret = spi_mem_exec_op(nor->spimem, &op);
  85. } else {
  86. ret = spi_nor_controller_ops_write_reg(nor,
  87. WINBOND_NOR_OP_SELDIE,
  88. nor->bouncebuf, 1);
  89. }
  90. if (ret)
  91. dev_dbg(nor->dev, "error %d selecting die %d\n", ret, die);
  92. return ret;
  93. }
  94. static int winbond_nor_multi_die_ready(struct spi_nor *nor)
  95. {
  96. int ret, i;
  97. for (i = 0; i < nor->params->n_dice; i++) {
  98. ret = winbond_nor_select_die(nor, i);
  99. if (ret)
  100. return ret;
  101. ret = spi_nor_sr_ready(nor);
  102. if (ret <= 0)
  103. return ret;
  104. }
  105. return 1;
  106. }
  107. static int
  108. winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor *nor)
  109. {
  110. /*
  111. * SFDP supports dice numbers, but this information is only available in
  112. * optional additional tables which are not provided by these chips.
  113. * Dice number has an impact though, because these devices need extra
  114. * care when reading the busy bit.
  115. */
  116. nor->params->n_dice = nor->params->size / SZ_64M;
  117. nor->params->ready = winbond_nor_multi_die_ready;
  118. return 0;
  119. }
  120. static const struct spi_nor_fixups winbond_nor_multi_die_fixups = {
  121. .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups,
  122. };
  123. static const struct flash_info winbond_nor_parts[] = {
  124. {
  125. .id = SNOR_ID(0xef, 0x30, 0x10),
  126. .name = "w25x05",
  127. .size = SZ_64K,
  128. .no_sfdp_flags = SECT_4K,
  129. }, {
  130. .id = SNOR_ID(0xef, 0x30, 0x11),
  131. .name = "w25x10",
  132. .size = SZ_128K,
  133. .no_sfdp_flags = SECT_4K,
  134. }, {
  135. .id = SNOR_ID(0xef, 0x30, 0x12),
  136. .name = "w25x20",
  137. .size = SZ_256K,
  138. .no_sfdp_flags = SECT_4K,
  139. }, {
  140. .id = SNOR_ID(0xef, 0x30, 0x13),
  141. .name = "w25x40",
  142. .size = SZ_512K,
  143. .no_sfdp_flags = SECT_4K,
  144. }, {
  145. .id = SNOR_ID(0xef, 0x30, 0x14),
  146. .name = "w25x80",
  147. .size = SZ_1M,
  148. .no_sfdp_flags = SECT_4K,
  149. }, {
  150. .id = SNOR_ID(0xef, 0x30, 0x15),
  151. .name = "w25x16",
  152. .size = SZ_2M,
  153. .no_sfdp_flags = SECT_4K,
  154. }, {
  155. .id = SNOR_ID(0xef, 0x30, 0x16),
  156. .name = "w25x32",
  157. .size = SZ_4M,
  158. .no_sfdp_flags = SECT_4K,
  159. }, {
  160. .id = SNOR_ID(0xef, 0x30, 0x17),
  161. .name = "w25x64",
  162. .size = SZ_8M,
  163. .no_sfdp_flags = SECT_4K,
  164. }, {
  165. .id = SNOR_ID(0xef, 0x40, 0x12),
  166. .name = "w25q20cl",
  167. .size = SZ_256K,
  168. .no_sfdp_flags = SECT_4K,
  169. }, {
  170. .id = SNOR_ID(0xef, 0x40, 0x14),
  171. .name = "w25q80bl",
  172. .size = SZ_1M,
  173. .no_sfdp_flags = SECT_4K,
  174. }, {
  175. .id = SNOR_ID(0xef, 0x40, 0x16),
  176. .name = "w25q32",
  177. .size = SZ_4M,
  178. .no_sfdp_flags = SECT_4K,
  179. }, {
  180. .id = SNOR_ID(0xef, 0x40, 0x17),
  181. .name = "w25q64",
  182. .size = SZ_8M,
  183. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  184. }, {
  185. .id = SNOR_ID(0xef, 0x40, 0x18),
  186. /* Flavors w/ and w/o SFDP. */
  187. .name = "w25q128",
  188. .size = SZ_16M,
  189. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  190. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  191. .fixups = &w25q128_fixups,
  192. }, {
  193. .id = SNOR_ID(0xef, 0x40, 0x19),
  194. .name = "w25q256",
  195. .size = SZ_32M,
  196. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  197. .fixups = &w25q256_fixups,
  198. }, {
  199. .id = SNOR_ID(0xef, 0x40, 0x20),
  200. .name = "w25q512jvq",
  201. .size = SZ_64M,
  202. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  203. }, {
  204. /* W25Q01JV */
  205. .id = SNOR_ID(0xef, 0x40, 0x21),
  206. .fixups = &winbond_nor_multi_die_fixups,
  207. }, {
  208. .id = SNOR_ID(0xef, 0x50, 0x12),
  209. .name = "w25q20bw",
  210. .size = SZ_256K,
  211. .no_sfdp_flags = SECT_4K,
  212. }, {
  213. .id = SNOR_ID(0xef, 0x50, 0x14),
  214. .name = "w25q80",
  215. .size = SZ_1M,
  216. .no_sfdp_flags = SECT_4K,
  217. }, {
  218. .id = SNOR_ID(0xef, 0x60, 0x12),
  219. .name = "w25q20ew",
  220. .size = SZ_256K,
  221. .no_sfdp_flags = SECT_4K,
  222. }, {
  223. .id = SNOR_ID(0xef, 0x60, 0x15),
  224. .name = "w25q16dw",
  225. .size = SZ_2M,
  226. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  227. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  228. }, {
  229. .id = SNOR_ID(0xef, 0x60, 0x16),
  230. .name = "w25q32dw",
  231. .size = SZ_4M,
  232. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  233. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  234. .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
  235. }, {
  236. .id = SNOR_ID(0xef, 0x60, 0x17),
  237. .name = "w25q64dw",
  238. .size = SZ_8M,
  239. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  240. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  241. }, {
  242. .id = SNOR_ID(0xef, 0x60, 0x18),
  243. .name = "w25q128fw",
  244. .size = SZ_16M,
  245. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  246. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  247. }, {
  248. .id = SNOR_ID(0xef, 0x60, 0x19),
  249. .name = "w25q256jw",
  250. .size = SZ_32M,
  251. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  252. }, {
  253. .id = SNOR_ID(0xef, 0x60, 0x20),
  254. .name = "w25q512nwq",
  255. .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
  256. }, {
  257. .id = SNOR_ID(0xef, 0x70, 0x15),
  258. .name = "w25q16jv-im/jm",
  259. .size = SZ_2M,
  260. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  261. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  262. }, {
  263. .id = SNOR_ID(0xef, 0x70, 0x16),
  264. .name = "w25q32jv",
  265. .size = SZ_4M,
  266. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  267. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  268. }, {
  269. .id = SNOR_ID(0xef, 0x70, 0x17),
  270. .name = "w25q64jvm",
  271. .size = SZ_8M,
  272. .no_sfdp_flags = SECT_4K,
  273. }, {
  274. .id = SNOR_ID(0xef, 0x70, 0x18),
  275. .name = "w25q128jv",
  276. .size = SZ_16M,
  277. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  278. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  279. }, {
  280. .id = SNOR_ID(0xef, 0x70, 0x19),
  281. .name = "w25q256jvm",
  282. }, {
  283. /* W25Q02JV */
  284. .id = SNOR_ID(0xef, 0x70, 0x22),
  285. .fixups = &winbond_nor_multi_die_fixups,
  286. }, {
  287. .id = SNOR_ID(0xef, 0x71, 0x19),
  288. .name = "w25m512jv",
  289. .size = SZ_64M,
  290. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  291. }, {
  292. .id = SNOR_ID(0xef, 0x80, 0x16),
  293. .name = "w25q32jwm",
  294. .size = SZ_4M,
  295. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  296. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  297. .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
  298. }, {
  299. .id = SNOR_ID(0xef, 0x80, 0x17),
  300. .name = "w25q64jwm",
  301. .size = SZ_8M,
  302. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  303. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  304. }, {
  305. .id = SNOR_ID(0xef, 0x80, 0x18),
  306. .name = "w25q128jwm",
  307. .size = SZ_16M,
  308. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  309. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  310. }, {
  311. .id = SNOR_ID(0xef, 0x80, 0x19),
  312. .name = "w25q256jwm",
  313. .size = SZ_32M,
  314. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
  315. .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
  316. }, {
  317. .id = SNOR_ID(0xef, 0x80, 0x20),
  318. .name = "w25q512nwm",
  319. .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
  320. }, {
  321. /* W25Q01NWxxIQ */
  322. .id = SNOR_ID(0xef, 0x60, 0x21),
  323. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
  324. }, {
  325. /* W25Q01NWxxIM */
  326. .id = SNOR_ID(0xef, 0x80, 0x21),
  327. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
  328. }, {
  329. /* W25Q02NWxxIM */
  330. .id = SNOR_ID(0xef, 0x80, 0x22),
  331. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
  332. }, {
  333. /* W25H512NWxxAM */
  334. .id = SNOR_ID(0xef, 0xa0, 0x20),
  335. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
  336. }, {
  337. /* W25H01NWxxAM */
  338. .id = SNOR_ID(0xef, 0xa0, 0x21),
  339. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
  340. }, {
  341. /* W25H02NWxxAM */
  342. .id = SNOR_ID(0xef, 0xa0, 0x22),
  343. .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
  344. },
  345. };
  346. /**
  347. * winbond_nor_write_ear() - Write Extended Address Register.
  348. * @nor: pointer to 'struct spi_nor'.
  349. * @ear: value to write to the Extended Address Register.
  350. *
  351. * Return: 0 on success, -errno otherwise.
  352. */
  353. static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
  354. {
  355. int ret;
  356. nor->bouncebuf[0] = ear;
  357. if (nor->spimem) {
  358. struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
  359. spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
  360. ret = spi_mem_exec_op(nor->spimem, &op);
  361. } else {
  362. ret = spi_nor_controller_ops_write_reg(nor,
  363. WINBOND_NOR_OP_WREAR,
  364. nor->bouncebuf, 1);
  365. }
  366. if (ret)
  367. dev_dbg(nor->dev, "error %d writing EAR\n", ret);
  368. return ret;
  369. }
  370. /**
  371. * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
  372. * flashes.
  373. * @nor: pointer to 'struct spi_nor'.
  374. * @enable: true to enter the 4-byte address mode, false to exit the 4-byte
  375. * address mode.
  376. *
  377. * Return: 0 on success, -errno otherwise.
  378. */
  379. static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
  380. {
  381. int ret;
  382. ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable);
  383. if (ret || enable)
  384. return ret;
  385. /*
  386. * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
  387. * Register to be set to 1, so all 3-byte-address reads come from the
  388. * second 16M. We must clear the register to enable normal behavior.
  389. */
  390. ret = spi_nor_write_enable(nor);
  391. if (ret)
  392. return ret;
  393. ret = winbond_nor_write_ear(nor, 0);
  394. if (ret)
  395. return ret;
  396. return spi_nor_write_disable(nor);
  397. }
  398. static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
  399. .read = spi_nor_otp_read_secr,
  400. .write = spi_nor_otp_write_secr,
  401. .erase = spi_nor_otp_erase_secr,
  402. .lock = spi_nor_otp_lock_sr2,
  403. .is_locked = spi_nor_otp_is_locked_sr2,
  404. };
  405. static int winbond_nor_late_init(struct spi_nor *nor)
  406. {
  407. struct spi_nor_flash_parameter *params = nor->params;
  408. if (params->otp.org)
  409. params->otp.ops = &winbond_nor_otp_ops;
  410. /*
  411. * Winbond seems to require that the Extended Address Register to be set
  412. * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV.
  413. * This requirement is not described in the JESD216 SFDP standard, thus
  414. * it is Winbond specific. Since we do not know if other Winbond flashes
  415. * have the same requirement, play safe and overwrite the method parsed
  416. * from BFPT, if any.
  417. */
  418. params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
  419. return 0;
  420. }
  421. static const struct spi_nor_fixups winbond_nor_fixups = {
  422. .late_init = winbond_nor_late_init,
  423. };
  424. const struct spi_nor_manufacturer spi_nor_winbond = {
  425. .name = "winbond",
  426. .parts = winbond_nor_parts,
  427. .nparts = ARRAY_SIZE(winbond_nor_parts),
  428. .fixups = &winbond_nor_fixups,
  429. };