wcxb_flash.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. /*
  2. * wcxb SPI library
  3. *
  4. * Copyright (C) 2013 Digium, Inc.
  5. *
  6. * All rights reserved.
  7. *
  8. */
  9. /*
  10. * See http://www.asterisk.org for more information about
  11. * the Asterisk project. Please do not directly contact
  12. * any of the maintainers of this project for assistance;
  13. * the project provides a web site, mailing lists and IRC
  14. * channels for your use.
  15. *
  16. * This program is free software, distributed under the terms of
  17. * the GNU General Public License Version 2 as published by the
  18. * Free Software Foundation. See the LICENSE file included with
  19. * this program for more details.
  20. */
  21. #include <linux/kernel.h>
  22. #include <linux/list.h>
  23. #include <linux/device.h>
  24. #include <linux/sched.h>
  25. #include "wcxb_spi.h"
  26. #include "wcxb_flash.h"
  27. #define FLASH_PAGE_PROGRAM 0x02
  28. #define FLASH_READ 0x03
  29. #define FLASH_READ_STATUS 0x05
  30. #define FLASH_WRITE_ENABLE 0x06
  31. #define FLASH_SECTOR_ERASE 0xd8
  32. static int wcxb_flash_read_status_register(struct wcxb_spi_device *spi,
  33. u8 *status)
  34. {
  35. u8 command[] = {
  36. FLASH_READ_STATUS,
  37. };
  38. struct wcxb_spi_transfer t_cmd = {
  39. .tx_buf = command,
  40. .len = sizeof(command),
  41. };
  42. struct wcxb_spi_transfer t_serial = {
  43. .rx_buf = status,
  44. .len = 1,
  45. };
  46. struct wcxb_spi_message m;
  47. wcxb_spi_message_init(&m);
  48. wcxb_spi_message_add_tail(&t_cmd, &m);
  49. wcxb_spi_message_add_tail(&t_serial, &m);
  50. return wcxb_spi_sync(spi, &m);
  51. }
  52. int wcxb_flash_read(struct wcxb_spi_device *spi, unsigned int address,
  53. void *data, size_t len)
  54. {
  55. u8 command[] = {
  56. FLASH_READ,
  57. (address & 0xff0000) >> 16,
  58. (address & 0xff00) >> 8,
  59. (address & 0xff)
  60. };
  61. struct wcxb_spi_transfer t_cmd = {
  62. .tx_buf = command,
  63. .len = sizeof(command),
  64. };
  65. struct wcxb_spi_transfer t_serial = {
  66. .rx_buf = data,
  67. .len = len,
  68. };
  69. struct wcxb_spi_message m;
  70. wcxb_spi_message_init(&m);
  71. wcxb_spi_message_add_tail(&t_cmd, &m);
  72. wcxb_spi_message_add_tail(&t_serial, &m);
  73. return wcxb_spi_sync(spi, &m);
  74. }
  75. static int wcxb_flash_wait_until_not_busy(struct wcxb_spi_device *spi)
  76. {
  77. int res;
  78. u8 status;
  79. unsigned long stop = jiffies + 5*HZ;
  80. do {
  81. res = wcxb_flash_read_status_register(spi, &status);
  82. } while (!res && (status & 0x1) && time_before(jiffies, stop));
  83. if (!res)
  84. return res;
  85. if (time_after_eq(jiffies, stop))
  86. return -EIO;
  87. return 0;
  88. }
  89. static int wcxb_flash_write_enable(struct wcxb_spi_device *spi)
  90. {
  91. u8 command = FLASH_WRITE_ENABLE;
  92. return wcxb_spi_write(spi, &command, 1);
  93. }
  94. int wcxb_flash_sector_erase(struct wcxb_spi_device *spi,
  95. unsigned int address)
  96. {
  97. int res;
  98. u8 command[] = {FLASH_SECTOR_ERASE, (address >> 16)&0xff, 0x00, 0x00};
  99. /* Sector must be on 64KB boundary. */
  100. if (address & 0xffff)
  101. return -EINVAL;
  102. /* Start the erase. */
  103. res = wcxb_flash_write_enable(spi);
  104. if (res)
  105. return res;
  106. res = wcxb_spi_write(spi, &command, sizeof(command));
  107. if (res)
  108. return res;
  109. return wcxb_flash_wait_until_not_busy(spi);
  110. }
  111. int wcxb_flash_write(struct wcxb_spi_device *spi, unsigned int address,
  112. const void *data, size_t len)
  113. {
  114. int res;
  115. const size_t FLASH_PAGE_SIZE = 256;
  116. u8 command[] = {
  117. FLASH_PAGE_PROGRAM,
  118. (address & 0xff0000) >> 16,
  119. (address & 0xff00) >> 8,
  120. 0x00,
  121. };
  122. struct wcxb_spi_transfer t_cmd = {
  123. .tx_buf = command,
  124. .len = sizeof(command),
  125. };
  126. struct wcxb_spi_transfer t_data = {
  127. .tx_buf = data,
  128. .len = len,
  129. };
  130. struct wcxb_spi_message m;
  131. /* We need to write on page size boundaries */
  132. WARN_ON(address & 0xff);
  133. wcxb_spi_message_init(&m);
  134. wcxb_spi_message_add_tail(&t_cmd, &m);
  135. wcxb_spi_message_add_tail(&t_data, &m);
  136. while (len) {
  137. res = wcxb_flash_write_enable(spi);
  138. if (res)
  139. return res;
  140. command[1] = (address >> 16) & 0xff;
  141. command[2] = (address >> 8) & 0xff;
  142. t_data.tx_buf = data;
  143. t_data.len = min(len, FLASH_PAGE_SIZE);
  144. res = wcxb_spi_sync(spi, &m);
  145. WARN_ON(res);
  146. if (res)
  147. return res;
  148. res = wcxb_flash_wait_until_not_busy(spi);
  149. WARN_ON(res);
  150. if (res)
  151. return res;
  152. len -= t_data.len;
  153. address += t_data.len;
  154. data += t_data.len;
  155. }
  156. return 0;
  157. }