You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

211 lines
7.1KB

  1. /***************************************************************************
  2. * Copyright (C) 2012 by George Harris *
  3. * george@luminairecoffee.com *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; either version 2 of the License, or *
  8. * (at your option) any later version. *
  9. * *
  10. * This program is distributed in the hope that it will be useful, *
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  13. * GNU General Public License for more details. *
  14. * *
  15. * You should have received a copy of the GNU General Public License *
  16. * along with this program; if not, write to the *
  17. * Free Software Foundation, Inc., *
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
  19. ***************************************************************************/
  20. .text
  21. .syntax unified
  22. .cpu cortex-m3
  23. .thumb
  24. .thumb_func
  25. /*
  26. * Params :
  27. * r0 = workarea start, status (out)
  28. * r1 = workarea end
  29. * r2 = target address (offset from flash base)
  30. * r3 = count (bytes)
  31. * r4 = page size
  32. * Clobbered:
  33. * r7 - rp
  34. * r8 - wp, tmp
  35. * r9 - send/receive data
  36. * r10 - temp
  37. * r11 - current page end address
  38. */
  39. #define SSP_BASE_HIGH 0x4008
  40. #define SSP_BASE_LOW 0x3000
  41. #define SSP_CR0_OFFSET 0x00
  42. #define SSP_CR1_OFFSET 0x04
  43. #define SSP_DATA_OFFSET 0x08
  44. #define SSP_CPSR_OFFSET 0x10
  45. #define SSP_SR_OFFSET 0x0c
  46. #define SSP_CLOCK_BASE_HIGH 0x4005
  47. #define SSP_CLOCK_BASE_LOW 0x0000
  48. #define SSP_BRANCH_CLOCK_BASE_HIGH 0x4005
  49. #define SSP_BRANCH_CLOCK_BASE_LOW 0x2000
  50. #define SSP_BASE_CLOCK_OFFSET 0x94
  51. #define SSP_BRANCH_CLOCK_OFFSET 0x700
  52. #define IOCONFIG_BASE_HIGH 0x4008
  53. #define IOCONFIG_BASE_LOW 0x6000
  54. #define IOCONFIG_SCK_OFFSET 0x18c
  55. #define IOCONFIG_HOLD_OFFSET 0x190
  56. #define IOCONFIG_WP_OFFSET 0x194
  57. #define IOCONFIG_MISO_OFFSET 0x198
  58. #define IOCONFIG_MOSI_OFFSET 0x19c
  59. #define IOCONFIG_CS_OFFSET 0x1a0
  60. #define IO_BASE_HIGH 0x400f
  61. #define IO_BASE_LOW 0x4000
  62. #define IO_CS_OFFSET 0xab
  63. #define IODIR_BASE_HIGH 0x400f
  64. #define IODIR_BASE_LOW 0x6000
  65. #define IO_CS_DIR_OFFSET 0x14
  66. setup: /* Initialize SSP pins and module */
  67. mov.w r10, #IOCONFIG_BASE_LOW
  68. movt r10, #IOCONFIG_BASE_HIGH
  69. mov.w r8, #0xea
  70. str.w r8, [r10, #IOCONFIG_SCK_OFFSET] /* Configure SCK pin function */
  71. mov.w r8, #0x40
  72. str.w r8, [r10, #IOCONFIG_HOLD_OFFSET] /* Configure /HOLD pin function */
  73. mov.w r8, #0x40
  74. str.w r8, [r10, #IOCONFIG_WP_OFFSET] /* Configure /WP pin function */
  75. mov.w r8, #0xed
  76. str.w r8, [r10, #IOCONFIG_MISO_OFFSET] /* Configure MISO pin function */
  77. mov.w r8, #0xed
  78. str.w r8, [r10, #IOCONFIG_MOSI_OFFSET] /* Configure MOSI pin function */
  79. mov.w r8, #0x44
  80. str.w r8, [r10, #IOCONFIG_CS_OFFSET] /* Configure CS pin function */
  81. mov.w r10, #IODIR_BASE_LOW
  82. movt r10, #IODIR_BASE_HIGH
  83. mov.w r8, #0x800
  84. str r8, [r10, #IO_CS_DIR_OFFSET] /* Set CS as output */
  85. mov.w r10, #IO_BASE_LOW
  86. movt r10, #IO_BASE_HIGH
  87. mov.w r8, #0xff
  88. str.w r8, [r10, #IO_CS_OFFSET] /* Set CS high */
  89. mov.w r10, #SSP_CLOCK_BASE_LOW
  90. movt r10, #SSP_CLOCK_BASE_HIGH
  91. mov.w r8, #0x0000
  92. movt r8, #0x0100
  93. str.w r8, [r10, #SSP_BASE_CLOCK_OFFSET] /* Configure SSP0 base clock (use 12 MHz IRC) */
  94. mov.w r10, #SSP_BRANCH_CLOCK_BASE_LOW
  95. movt r10, #SSP_BRANCH_CLOCK_BASE_HIGH
  96. mov.w r8, #0x01
  97. str.w r8, [r10, #SSP_BRANCH_CLOCK_OFFSET] /* Configure (enable) SSP0 branch clock */
  98. mov.w r10, #SSP_BASE_LOW
  99. movt r10, #SSP_BASE_HIGH
  100. mov.w r8, #0x07
  101. str.w r8, [r10, #SSP_CR0_OFFSET] /* Set clock postscale */
  102. mov.w r8, #0x02
  103. str.w r8, [r10, #SSP_CPSR_OFFSET] /* Set clock prescale */
  104. str.w r8, [r10, #SSP_CR1_OFFSET] /* Enable SSP in SPI mode */
  105. mov.w r11, #0x00
  106. find_next_page_boundary:
  107. add r11, r4 /* Increment to the next page */
  108. cmp r11, r2
  109. /* If we have not reached the next page boundary after the target address, keep going */
  110. bls find_next_page_boundary
  111. write_enable:
  112. bl cs_down
  113. mov.w r9, #0x06 /* Send the write enable command */
  114. bl write_data
  115. bl cs_up
  116. bl cs_down
  117. mov.w r9, #0x05 /* Get status register */
  118. bl write_data
  119. mov.w r9, #0x00 /* Dummy data to clock in status */
  120. bl write_data
  121. bl cs_up
  122. tst r9, #0x02 /* If the WE bit isn't set, we have a problem. */
  123. beq error
  124. page_program:
  125. bl cs_down
  126. mov.w r9, #0x02 /* Send the page program command */
  127. bl write_data
  128. write_address:
  129. lsr r9, r2, #16 /* Send the current 24-bit write address, MSB first */
  130. bl write_data
  131. lsr r9, r2, #8
  132. bl write_data
  133. mov.w r9, r2
  134. bl write_data
  135. wait_fifo:
  136. ldr r8, [r0] /* read the write pointer */
  137. cmp r8, #0 /* if it's zero, we're gonzo */
  138. beq exit
  139. ldr r7, [r0, #4] /* read the read pointer */
  140. cmp r7, r8 /* wait until they are not equal */
  141. beq wait_fifo
  142. write:
  143. ldrb r9, [r7], #0x01 /* Load one byte from the FIFO, increment the read pointer by 1 */
  144. bl write_data /* send the byte to the flash chip */
  145. cmp r7, r1 /* wrap the read pointer if it is at the end */
  146. it cs
  147. addcs r7, r0, #8 /* skip loader args */
  148. str r7, [r0, #4] /* store the new read pointer */
  149. subs r3, r3, #1 /* decrement count */
  150. cbz r3, exit /* Exit if we have written everything */
  151. add r2, #1 /* Increment flash address by 1 */
  152. cmp r11, r2 /* See if we have reached the end of a page */
  153. bne wait_fifo /* If not, keep writing bytes */
  154. bl cs_up /* Otherwise, end the command and keep going w/ the next page */
  155. add r11, r4 /* Move up the end-of-page address by the page size*/
  156. wait_flash_busy: /* Wait for the flash to finish the previous page write */
  157. bl cs_down
  158. mov.w r9, #0x05 /* Get status register */
  159. bl write_data
  160. mov.w r9, #0x00 /* Dummy data to clock in status */
  161. bl write_data
  162. bl cs_up
  163. tst r9, #0x01 /* If it isn't done, keep waiting */
  164. bne wait_flash_busy
  165. b write_enable /* If it is done, start a new page write */
  166. write_data: /* Send/receive 1 byte of data over SSP */
  167. mov.w r10, #SSP_BASE_LOW
  168. movt r10, #SSP_BASE_HIGH
  169. str.w r9, [r10, #SSP_DATA_OFFSET] /* Write supplied data to the SSP data reg */
  170. wait_transmit:
  171. ldr r9, [r10, #SSP_SR_OFFSET] /* Check SSP status */
  172. tst r9, #0x0010 /* Check if BSY bit is set */
  173. bne wait_transmit /* If still transmitting, keep waiting */
  174. ldr r9, [r10, #SSP_DATA_OFFSET] /* Load received data */
  175. bx lr /* Exit subroutine */
  176. cs_up:
  177. mov.w r8, #0xff
  178. b cs_write
  179. cs_down:
  180. mov.w r8, #0x0000
  181. cs_write:
  182. mov.w r10, #IO_BASE_LOW
  183. movt r10, #IO_BASE_HIGH
  184. str.w r8, [r10, #IO_CS_OFFSET]
  185. bx lr
  186. error:
  187. movs r0, #0
  188. str r0, [r2, #4] /* set rp = 0 on error */
  189. exit:
  190. mov r0, r6
  191. bkpt #0x00
  192. .end