ls1c_spi.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. /*
  2. * File : ls1c_spi.c
  3. * This file is part of RT-Thread RTOS
  4. * COPYRIGHT (C) 2006 - 2012, RT-Thread Development Team
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. *
  20. * Change Logs:
  21. * Date Author Notes
  22. * 2017-10-23 勤为本 first version
  23. */
  24. // 硬件spi接口源文件
  25. #include <string.h>
  26. #include "ls1c_public.h"
  27. #include "ls1c_regs.h"
  28. #include "ls1c_clock.h"
  29. #include "ls1c_spi.h"
  30. /*
  31. * 获取指定SPI模块的基地址
  32. * @SPIx SPI模块的编号
  33. */
  34. inline void *ls1c_spi_get_base(unsigned char SPIx)
  35. {
  36. void *base = NULL;
  37. switch (SPIx)
  38. {
  39. case LS1C_SPI_0:
  40. base = (void *)LS1C_SPI0_BASE;
  41. break;
  42. case LS1C_SPI_1:
  43. base = (void *)LS1C_SPI1_BASE;
  44. break;
  45. default:
  46. base = NULL;
  47. break;
  48. }
  49. return base;
  50. }
  51. /*
  52. * 打印指定SPI模块的所有寄存器的值
  53. * @spi_base 基地址
  54. */
  55. void ls1c_spi_print_all_regs_info(void *spi_base)
  56. {
  57. rt_kprintf("[%s] SPCR=0x%x, SPSR=0x%x, SPER=0x%x, SFC_PARAM=0x%x, SFC_SOFTCS=0x%x, SFC_TIMING=0x%x\r\n",
  58. __FUNCTION__,
  59. reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET),
  60. reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET),
  61. reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET),
  62. reg_read_8(spi_base + LS1C_SPI_SFC_PARAM_OFFSET),
  63. reg_read_8(spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET),
  64. reg_read_8(spi_base + LS1C_SPI_SFC_TIMING_OFFSET));
  65. return ;
  66. }
  67. /*
  68. * 根据SPI时钟频率计算分频系数
  69. * @max_speed_hz SPI最大通信速度
  70. * @ret 分频系数
  71. */
  72. unsigned int ls1c_spi_get_div(unsigned int max_speed_hz)
  73. {
  74. unsigned long clk = 0;
  75. unsigned int div = 0;
  76. unsigned int div_tmp = 0;
  77. unsigned int bit = 0;
  78. clk = clk_get_apb_rate();
  79. div = DIV_ROUND_UP(clk, max_speed_hz);
  80. if (div < 2)
  81. div = 2;
  82. if (div > 4096)
  83. div = 4096;
  84. bit = ls1c_fls(div) - 1;
  85. switch (1 << bit)
  86. {
  87. case 16:
  88. div_tmp = 2;
  89. if (div > (1 << bit))
  90. {
  91. div_tmp++;
  92. }
  93. break;
  94. case 32:
  95. div_tmp = 3;
  96. if (div > (1 << bit))
  97. {
  98. div_tmp += 2;
  99. }
  100. break;
  101. case 8:
  102. div_tmp = 4;
  103. if (div > (1 << bit))
  104. {
  105. div_tmp -= 2;
  106. }
  107. break;
  108. default:
  109. div_tmp = bit - 1;
  110. if (div > (1 << bit))
  111. {
  112. div_tmp++;
  113. }
  114. break;
  115. }
  116. /*
  117. rt_kprintf("[%s] clk=%ld, max_speed_hz=%d, div_tmp=%d, bit=%d\r\n",
  118. __FUNCTION__, clk, max_speed_hz, div_tmp, bit);
  119. */
  120. return div_tmp;
  121. }
  122. /*
  123. * 设置时钟
  124. * @spi_base 基地址
  125. * @max_hz 最大频率,单位hz
  126. */
  127. void ls1c_spi_set_clock(void *spi_base, unsigned long max_hz)
  128. {
  129. unsigned int div = 0;
  130. unsigned char val = 0;
  131. // 获取分频系数
  132. div = ls1c_spi_get_div(max_hz);
  133. // 设置spr
  134. val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
  135. val &= (~LS1C_SPI_SPCR_SPR_MASK); // spr清零
  136. val |= (div & LS1C_SPI_SPCR_SPR_MASK); // 设置新的spr
  137. reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);
  138. // 设置spre
  139. val = reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET);
  140. val &= (~LS1C_SPI_SPER_SPRE_MASK); // spre清零
  141. val |= ((div >> 2) & LS1C_SPI_SPER_SPRE_MASK); // 设置新的spre
  142. reg_write_8(val, spi_base + LS1C_SPI_SPER_OFFSET);
  143. return ;
  144. }
  145. /*
  146. * 设置通信模式(时钟极性和相位)
  147. * @spi_base 基地址
  148. * @cpol 时钟极性
  149. * @cpha 时钟相位
  150. */
  151. void ls1c_spi_set_mode(void *spi_base, unsigned char cpol, unsigned char cpha)
  152. {
  153. unsigned char val = 0;
  154. val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
  155. // 设置时钟极性--cpol
  156. val &= (~LS1C_SPI_SPCR_CPOL_MASK); // cpol清0
  157. val |= (cpol << LS1C_SPI_SPCR_CPOL_BIT); // 写入新的cpol
  158. // 设置时钟相位--cpha
  159. val &= (~LS1C_SPI_SPCR_CPHA_MASK); // cpha清0
  160. val |= (cpha << LS1C_SPI_SPCR_CPHA_BIT); // 写入新的cpha
  161. reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);
  162. return ;
  163. }
  164. /*
  165. * 设置指定片选为指定状态
  166. * @spi_base 基地址
  167. * @cs 片选
  168. * @new_status 片选引脚的新状态,取值为0或1,即高电平或低电平
  169. */
  170. void ls1c_spi_set_cs(void *spi_base, unsigned char cs, int new_status)
  171. {
  172. unsigned char val = 0;
  173. val = reg_read_8(spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET);
  174. val |= 0x01 << cs ; //对应的csen=1
  175. if (new_status) // cs = 1
  176. {
  177. val |= (0x10 << cs); // 指定csn=1
  178. }
  179. else // cs = 0
  180. {
  181. val &= ~(0x10 << cs); // 指定csn=0
  182. }
  183. reg_write_8(val, spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET);
  184. return ;
  185. }
  186. /*
  187. * 等待收发完成
  188. * @spi_base 基地址
  189. */
  190. inline void ls1c_spi_wait_txrx_done(void *spi_base)
  191. {
  192. int timeout = LS1C_SPI_TX_TIMEOUT;
  193. while (timeout--)
  194. {
  195. if (LS1C_SPI_SPSR_SPIF_MASK & reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET))
  196. break;
  197. }
  198. return ;
  199. }
  200. /*
  201. * 清中断和标志位
  202. * @spi_base 基地址
  203. */
  204. inline void ls1c_spi_clear(void *spi_base)
  205. {
  206. unsigned char val = 0;
  207. // 清中断
  208. val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
  209. val |= LS1C_SPI_SPSR_SPIF_MASK;
  210. reg_write_8(val, spi_base + LS1C_SPI_SPSR_OFFSET);
  211. // 清溢出标志位(Write-Collision Clear)
  212. val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
  213. if (LS1C_SPI_SPSR_WCOL_MASK & val)
  214. {
  215. rt_kprintf("[%s] clear register SPSR's wcol!\r\n"); // 手册和linux源码中不一样,加个打印看看
  216. reg_write_8(val & ~LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET); // 写0,linux源码中是写0
  217. // reg_write_8(val | LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET); // 写1,按照1c手册,应该写1
  218. }
  219. return ;
  220. }
  221. /*
  222. * 通过指定SPI发送接收一个字节
  223. * 注意,在多任务的系统中,此函数需要互斥。
  224. * 即保证在和某个从设备收发某个字节的过程中,不能被切换到其它任务同时与另外的在同一个SPI总线上的从设备通信
  225. * 因为龙芯1c的每路SPI上可能接有不同的从设备,通信频率、模式等可能不同
  226. * @spi_base 基地址
  227. * @tx_ch 待发送的数据
  228. * @ret 收到的数据
  229. */
  230. unsigned char ls1c_spi_txrx_byte(void *spi_base, unsigned char tx_ch)
  231. {
  232. unsigned char rx_ch = 0;
  233. // 收发数据
  234. reg_write_8(tx_ch, spi_base + LS1C_SPI_TxFIFO_OFFSET); // 开始发送
  235. ls1c_spi_wait_txrx_done(spi_base); // 等待收发完成
  236. rx_ch = reg_read_8(spi_base + LS1C_SPI_RxFIFO_OFFSET); // 读取收到的数据
  237. ls1c_spi_clear(spi_base); // 清中断和标志位
  238. return rx_ch;
  239. }