ls1c_spi.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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. #define LS1C_SPI_SPCR_OFFSET (0) // 控制寄存器
  32. #define LS1C_SPI_SPSR_OFFSET (1) // 状态寄存器
  33. #define LS1C_SPI_TxFIFO_OFFSET (2) // 发送的数据寄存器,与接收数据寄存器的偏移相同
  34. #define LS1C_SPI_RxFIFO_OFFSET (2) // 接收的数据寄存器,与发送数据寄存器的偏移相同
  35. #define LS1C_SPI_SPER_OFFSET (3) // 外部寄存器
  36. #define LS1C_SPI_SFC_PARAM_OFFSET (4) // 参数控制寄存器
  37. #define LS1C_SPI_SFC_SOFTCS_OFFSET (5) // 片选控制寄存器
  38. #define LS1C_SPI_SFC_TIMING_OFFSET (6) // 时序控制寄存器
  39. // 寄存器SPCR中的位域
  40. #define LS1C_SPI_SPCR_SPIE_BIT (7)
  41. #define LS1C_SPI_SPCR_SPIE_MASK (0x01 << LS1C_SPI_SPCR_SPIE_BIT)
  42. #define LS1C_SPI_SPCR_SPE_BIT (6)
  43. #define LS1C_SPI_SPCR_SPE_MASK (0x01 << LS1C_SPI_SPCR_SPE_BIT)
  44. #define LS1C_SPI_SPCR_CPOL_BIT (3)
  45. #define LS1C_SPI_SPCR_CPOL_MASK (0x01 << LS1C_SPI_SPCR_CPOL_BIT)
  46. #define LS1C_SPI_SPCR_CPHA_BIT (2)
  47. #define LS1C_SPI_SPCR_CPHA_MASK (0x01 << LS1C_SPI_SPCR_CPHA_BIT)
  48. #define LS1C_SPI_SPCR_SPR_BIT (0)
  49. #define LS1C_SPI_SPCR_SPR_MASK (0x03 << LS1C_SPI_SPCR_SPR_BIT)
  50. // 寄存器SPSR中的位域
  51. #define LS1C_SPI_SPSR_SPIF_BIT (7)
  52. #define LS1C_SPI_SPSR_SPIF_MASK (0x01 << LS1C_SPI_SPSR_SPIF_BIT)
  53. #define LS1C_SPI_SPSR_WCOL_BIT (6)
  54. #define LS1C_SPI_SPSR_WCOL_MASK (0x01 << LS1C_SPI_SPSR_WCOL_BIT)
  55. // 寄存器SPER中的位域
  56. #define LS1C_SPI_SPER_SPRE_BIT (0)
  57. #define LS1C_SPI_SPER_SPRE_MASK (0x3 << LS1C_SPI_SPER_SPRE_BIT)
  58. // 寄存器SFC_SOFTCS的位域
  59. #define LS1C_SPI_SFC_SOFTCS_CSN_BIT (4)
  60. #define LS1C_SPI_SFC_SOFTCS_CSN_MASK (0x0f << LS1C_SPI_SFC_SOFTCS_CSN_BIT)
  61. #define LS1C_SPI_SFC_SOFTCS_CSEN_BIT (0)
  62. #define LS1C_SPI_SFC_SOFTCS_CSEN_MASK (0x0f << LS1C_SPI_SFC_SOFTCS_CSEN_BIT)
  63. // 发送超时的门限值
  64. #define LS1C_SPI_TX_TIMEOUT (20000)
  65. /*
  66. * 获取指定SPI模块的基地址
  67. * @SPIx SPI模块的编号
  68. */
  69. inline void *spi_get_base(ls1c_spi_t SPIx)
  70. {
  71. void *base = NULL;
  72. switch (SPIx)
  73. {
  74. case LS1C_SPI_0:
  75. base = (void *)LS1C_SPI0_BASE;
  76. break;
  77. case LS1C_SPI_1:
  78. base = (void *)LS1C_SPI1_BASE;
  79. break;
  80. default:
  81. base = NULL;
  82. break;
  83. }
  84. return base;
  85. }
  86. /*
  87. * 打印指定SPI模块的所有寄存器的值
  88. * @spi_info_p SPI模块信息
  89. */
  90. void spi_print_all_regs_info(ls1c_spi_info_t *spi_info_p)
  91. {
  92. void *spi_base = spi_get_base(spi_info_p->SPIx);
  93. rt_kprintf("[%s] SPI%d's info:\r\n\
  94. SPCR=0x%x, SPSR=0x%x, SPER=0x%x, SFC_PARAM=0x%x, SFC_SOFTCS=0x%x, SFC_TIMING=0x%x\r\n",
  95. __FUNCTION__, spi_info_p->SPIx,
  96. reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET),
  97. reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET),
  98. reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET),
  99. reg_read_8(spi_base + LS1C_SPI_SFC_PARAM_OFFSET),
  100. reg_read_8(spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET),
  101. reg_read_8(spi_base + LS1C_SPI_SFC_TIMING_OFFSET));
  102. return ;
  103. }
  104. /*
  105. * 根据SPI时钟频率计算分频系数
  106. * @max_speed_hz SPI最大通信速度
  107. * @ret 分频系数
  108. */
  109. unsigned int spi_get_div(unsigned int max_speed_hz)
  110. {
  111. unsigned long clk = 0;
  112. unsigned int div = 0;
  113. unsigned int div_tmp = 0;
  114. unsigned int bit = 0;
  115. clk = clk_get_apb_rate();
  116. div = DIV_ROUND_UP(clk, max_speed_hz);
  117. if (div < 2)
  118. div = 2;
  119. if (div > 4096)
  120. div = 4096;
  121. bit = ls1c_fls(div) - 1;
  122. switch (1 << bit)
  123. {
  124. case 16:
  125. div_tmp = 2;
  126. if (div > (1 << bit))
  127. {
  128. div_tmp++;
  129. }
  130. break;
  131. case 32:
  132. div_tmp = 3;
  133. if (div > (1 << bit))
  134. {
  135. div_tmp += 2;
  136. }
  137. break;
  138. case 8:
  139. div_tmp = 4;
  140. if (div > (1 << bit))
  141. {
  142. div_tmp -= 2;
  143. }
  144. break;
  145. default:
  146. div_tmp = bit - 1;
  147. if (div > (1 << bit))
  148. {
  149. div_tmp++;
  150. }
  151. break;
  152. }
  153. /*
  154. rt_kprintf("[%s] clk=%ld, max_speed_hz=%d, div_tmp=%d, bit=%d\r\n",
  155. __FUNCTION__, clk, max_speed_hz, div_tmp, bit);
  156. */
  157. return div_tmp;
  158. }
  159. /*
  160. * 设置时钟
  161. * @spi_info_p SPI模块信息
  162. */
  163. void spi_set_clock(ls1c_spi_info_t *spi_info_p)
  164. {
  165. void *spi_base = spi_get_base(spi_info_p->SPIx);
  166. unsigned int div = 0;
  167. unsigned char val = 0;
  168. // 获取分频系数
  169. div = spi_get_div(spi_info_p->max_speed_hz);
  170. // 设置spr
  171. val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
  172. val &= (~LS1C_SPI_SPCR_SPR_MASK); // spr清零
  173. val |= (div & LS1C_SPI_SPCR_SPR_MASK); // 设置新的spr
  174. reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);
  175. // 设置spre
  176. val = reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET);
  177. val &= (~LS1C_SPI_SPER_SPRE_MASK); // spre清零
  178. val |= ((div >> 2) & LS1C_SPI_SPER_SPRE_MASK); // 设置新的spre
  179. reg_write_8(val, spi_base + LS1C_SPI_SPER_OFFSET);
  180. return ;
  181. }
  182. /*
  183. * 设置通信模式(时钟极性和相位)
  184. * @spi_info_p SPI模块信息
  185. */
  186. void spi_set_mode(ls1c_spi_info_t *spi_info_p)
  187. {
  188. void *spi_base = spi_get_base(spi_info_p->SPIx);
  189. unsigned char val = 0;
  190. val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
  191. // 设置时钟极性--cpol
  192. val &= (~LS1C_SPI_SPCR_CPOL_MASK); // cpol清0
  193. val |= (spi_info_p->cpol << LS1C_SPI_SPCR_CPOL_BIT); // 写入新的cpol
  194. // 设置时钟相位--cpha
  195. val &= (~LS1C_SPI_SPCR_CPHA_MASK); // cpha清0
  196. val |= (spi_info_p->cpha << LS1C_SPI_SPCR_CPHA_BIT); // 写入新的cpha
  197. reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);
  198. return ;
  199. }
  200. /*
  201. * 设置指定片选为指定状态
  202. * @spi_info_p SPI模块信息
  203. * @new_status 片选引脚的新状态,取值为0或1,即高电平或低电平
  204. */
  205. void spi_set_cs(ls1c_spi_info_t *spi_info_p, int new_status)
  206. {
  207. void *spi_base = spi_get_base(spi_info_p->SPIx);
  208. unsigned char cs = spi_info_p->cs;
  209. unsigned char val = 0;
  210. val = 0xf0 | (0x01 << cs); // 全部csn=1,指定的csen=1
  211. if (new_status) // cs = 1
  212. {
  213. val |= (0x10 << cs); // 指定csn=1
  214. }
  215. else // cs = 0
  216. {
  217. val &= ~(0x10 << cs); // 指定csn=0
  218. }
  219. reg_write_8(val, spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET);
  220. return ;
  221. }
  222. /*
  223. * 初始化指定SPI模块
  224. * @spi_info_p SPI模块信息
  225. */
  226. void spi_init(ls1c_spi_info_t *spi_info_p)
  227. {
  228. void *spi_base = spi_get_base(spi_info_p->SPIx);
  229. unsigned char val = 0;
  230. // 使能SPI控制器,master模式,关闭中断
  231. reg_write_8(0x53, spi_base + LS1C_SPI_SPCR_OFFSET);
  232. // 清空状态寄存器
  233. reg_write_8(0xc0, spi_base + LS1C_SPI_SPSR_OFFSET);
  234. // 1字节产生中断,采样(读)与发送(写)时机同时
  235. reg_write_8(0x03, spi_base + LS1C_SPI_SPER_OFFSET);
  236. // 关闭SPI flash
  237. val = reg_read_8(spi_base + LS1C_SPI_SFC_PARAM_OFFSET);
  238. val &= 0xfe;
  239. reg_write_8(val, spi_base + LS1C_SPI_SFC_PARAM_OFFSET);
  240. // spi flash时序控制寄存器
  241. reg_write_8(0x05, spi_base + LS1C_SPI_SFC_TIMING_OFFSET);
  242. // 设置时钟
  243. spi_set_clock(spi_info_p);
  244. // 设置通信模式(时钟极性和相位)
  245. spi_set_mode(spi_info_p);
  246. // 打印寄存器信息(用于调试)
  247. // spi_print_all_regs_info(spi_info_p);
  248. return ;
  249. }
  250. /*
  251. * 等待收发完成
  252. */
  253. inline void spi_wait_txrx_done(ls1c_spi_info_t *spi_info_p)
  254. {
  255. void *spi_base = spi_get_base(spi_info_p->SPIx);
  256. int timeout = LS1C_SPI_TX_TIMEOUT;
  257. while (timeout--)
  258. {
  259. if (LS1C_SPI_SPSR_SPIF_MASK & reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET))
  260. break;
  261. }
  262. return ;
  263. }
  264. /*
  265. * 清中断和标志位
  266. */
  267. inline void spi_clear(ls1c_spi_info_t *spi_info_p)
  268. {
  269. void *spi_base = spi_get_base(spi_info_p->SPIx);
  270. unsigned char val = 0;
  271. // 清中断
  272. val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
  273. val |= LS1C_SPI_SPSR_SPIF_MASK;
  274. reg_write_8(val, spi_base + LS1C_SPI_SPSR_OFFSET);
  275. // 清溢出标志位(Write-Collision Clear)
  276. val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
  277. if (LS1C_SPI_SPSR_WCOL_MASK & val)
  278. {
  279. rt_kprintf("[%s] clear register SPSR's wcol!\r\n"); // 手册和linux源码中不一样,加个打印看看
  280. reg_write_8(val & ~LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET); // 写0,linux源码中是写0
  281. // reg_write_8(val | LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET); // 写1,按照1c手册,应该写1
  282. }
  283. return ;
  284. }
  285. /*
  286. * 通过指定SPI发送接收一个字节
  287. * 注意,在多任务的系统中,此函数需要互斥。
  288. * 即保证在和某个从设备收发某个字节的过程中,不能被切换到其它任务同时与另外的在同一个SPI总线上的从设备通信
  289. * 因为龙芯1c的每路SPI上可能接有不同的从设备,通信频率、模式等可能不同
  290. * @spi_info_p SPI接口
  291. * @tx_ch 待发送的数据
  292. * @ret 收到的数据
  293. */
  294. unsigned char spi_txrx_byte(ls1c_spi_info_t *spi_info_p, unsigned char tx_ch)
  295. {
  296. void *spi_base = spi_get_base(spi_info_p->SPIx);
  297. unsigned char rx_ch = 0;
  298. // 收发数据
  299. reg_write_8(tx_ch, spi_base + LS1C_SPI_TxFIFO_OFFSET); // 开始发送
  300. spi_wait_txrx_done(spi_info_p); // 等待收发完成
  301. rx_ch = reg_read_8(spi_base + LS1C_SPI_RxFIFO_OFFSET); // 读取收到的数据
  302. spi_clear(spi_info_p); // 清中断和标志位
  303. return rx_ch;
  304. }