1 /*
2 * Copyright (c) 2006-2018, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2017-10-23 勤为本 first version
9 */
10
11 // 硬件spi接口源文件
12
13 #include <string.h>
14 #include "ls1c_public.h"
15 #include "ls1c_regs.h"
16 #include "ls1c_clock.h"
17 #include "ls1c_spi.h"
18
19
20
21
22 /*
23 * 获取指定SPI模块的基地址
24 * @SPIx SPI模块的编号
25 */
ls1c_spi_get_base(unsigned char SPIx)26 void *ls1c_spi_get_base(unsigned char SPIx)
27 {
28 void *base = NULL;
29
30 switch (SPIx)
31 {
32 case LS1C_SPI_0:
33 base = (void *)LS1C_SPI0_BASE;
34 break;
35
36 case LS1C_SPI_1:
37 base = (void *)LS1C_SPI1_BASE;
38 break;
39
40 default:
41 base = NULL;
42 break;
43 }
44
45 return base;
46 }
47
48
49 /*
50 * 打印指定SPI模块的所有寄存器的值
51 * @spi_base 基地址
52 */
ls1c_spi_print_all_regs_info(void * spi_base)53 void ls1c_spi_print_all_regs_info(void *spi_base)
54 {
55 printf("[%s] SPCR=0x%x, SPSR=0x%x, SPER=0x%x, SFC_PARAM=0x%x, SFC_SOFTCS=0x%x, SFC_TIMING=0x%x\r\n",
56 __FUNCTION__,
57 reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET),
58 reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET),
59 reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET),
60 reg_read_8(spi_base + LS1C_SPI_SFC_PARAM_OFFSET),
61 reg_read_8(spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET),
62 reg_read_8(spi_base + LS1C_SPI_SFC_TIMING_OFFSET));
63
64 return ;
65 }
66
67
68 /*
69 * 根据SPI时钟频率计算分频系数
70 * @max_speed_hz SPI最大通信速度
71 * @ret 分频系数
72 */
ls1c_spi_get_div(unsigned int max_speed_hz)73 unsigned int ls1c_spi_get_div(unsigned int max_speed_hz)
74 {
75 unsigned long clk = 0;
76 unsigned int div = 0;
77 unsigned int div_tmp = 0;
78 unsigned int bit = 0;
79
80 clk = clk_get_apb_rate();
81 div = DIV_ROUND_UP(clk, max_speed_hz);
82
83 if (div < 2)
84 div = 2;
85
86 if (div > 4096)
87 div = 4096;
88
89 bit = ls1c_fls(div) - 1;
90 switch (1 << bit)
91 {
92 case 16:
93 div_tmp = 2;
94 if (div > (1 << bit))
95 {
96 div_tmp++;
97 }
98 break;
99
100 case 32:
101 div_tmp = 3;
102 if (div > (1 << bit))
103 {
104 div_tmp += 2;
105 }
106 break;
107
108 case 8:
109 div_tmp = 4;
110 if (div > (1 << bit))
111 {
112 div_tmp -= 2;
113 }
114 break;
115
116 default:
117 div_tmp = bit - 1;
118 if (div > (1 << bit))
119 {
120 div_tmp++;
121 }
122 break;
123 }
124 /*
125 printf("[%s] clk=%ld, max_speed_hz=%d, div_tmp=%d, bit=%d\r\n",
126 __FUNCTION__, clk, max_speed_hz, div_tmp, bit);
127 */
128 return div_tmp;
129 }
130
131
132 /*
133 * 设置时钟
134 * @spi_base 基地址
135 * @max_hz 最大频率,单位hz
136 */
ls1c_spi_set_clock(void * spi_base,unsigned long max_hz)137 void ls1c_spi_set_clock(void *spi_base, unsigned long max_hz)
138 {
139 unsigned int div = 0;
140 unsigned char val = 0;
141
142 // 获取分频系数
143 div = ls1c_spi_get_div(max_hz);
144
145 // 设置spr
146 val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
147 val &= (~LS1C_SPI_SPCR_SPR_MASK); // spr清零
148 val |= (div & LS1C_SPI_SPCR_SPR_MASK); // 设置新的spr
149 reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);
150
151 // 设置spre
152 val = reg_read_8(spi_base + LS1C_SPI_SPER_OFFSET);
153 val &= (~LS1C_SPI_SPER_SPRE_MASK); // spre清零
154 val |= ((div >> 2) & LS1C_SPI_SPER_SPRE_MASK); // 设置新的spre
155 reg_write_8(val, spi_base + LS1C_SPI_SPER_OFFSET);
156
157 return ;
158 }
159
160
161 /*
162 * 设置通信模式(时钟极性和相位)
163 * @spi_base 基地址
164 * @cpol 时钟极性
165 * @cpha 时钟相位
166 */
ls1c_spi_set_mode(void * spi_base,unsigned char cpol,unsigned char cpha)167 void ls1c_spi_set_mode(void *spi_base, unsigned char cpol, unsigned char cpha)
168 {
169 unsigned char val = 0;
170
171 val = reg_read_8(spi_base + LS1C_SPI_SPCR_OFFSET);
172
173 // 设置时钟极性--cpol
174 val &= (~LS1C_SPI_SPCR_CPOL_MASK); // cpol清0
175 val |= (cpol << LS1C_SPI_SPCR_CPOL_BIT); // 写入新的cpol
176
177 // 设置时钟相位--cpha
178 val &= (~LS1C_SPI_SPCR_CPHA_MASK); // cpha清0
179 val |= (cpha << LS1C_SPI_SPCR_CPHA_BIT); // 写入新的cpha
180
181 reg_write_8(val, spi_base + LS1C_SPI_SPCR_OFFSET);
182
183 return ;
184 }
185
186
187 /*
188 * 设置指定片选为指定状态
189 * @spi_base 基地址
190 * @cs 片选
191 * @new_status 片选引脚的新状态,取值为0或1,即高电平或低电平
192 */
ls1c_spi_set_cs(void * spi_base,unsigned char cs,int new_status)193 void ls1c_spi_set_cs(void *spi_base, unsigned char cs, int new_status)
194 {
195 unsigned char val = 0;
196
197 val = reg_read_8(spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET);
198 val |= 0x01 << cs ; //对应的csen=1
199 if (new_status) // cs = 1
200 {
201 val |= (0x10 << cs); // 指定csn=1
202 }
203 else // cs = 0
204 {
205 val &= ~(0x10 << cs); // 指定csn=0
206 }
207 reg_write_8(val, spi_base + LS1C_SPI_SFC_SOFTCS_OFFSET);
208
209 return ;
210 }
211
212
213 /*
214 * 等待收发完成
215 * @spi_base 基地址
216 */
ls1c_spi_wait_txrx_done(void * spi_base)217 void ls1c_spi_wait_txrx_done(void *spi_base)
218 {
219 int timeout = LS1C_SPI_TX_TIMEOUT;
220
221 while (timeout--)
222 {
223 if (LS1C_SPI_SPSR_SPIF_MASK & reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET))
224 break;
225 }
226
227 return ;
228 }
229
230
231 /*
232 * 清中断和标志位
233 * @spi_base 基地址
234 */
ls1c_spi_clear(void * spi_base)235 void ls1c_spi_clear(void *spi_base)
236 {
237 unsigned char val = 0;
238
239 // 清中断
240 val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
241 val |= LS1C_SPI_SPSR_SPIF_MASK;
242 reg_write_8(val, spi_base + LS1C_SPI_SPSR_OFFSET);
243
244 // 清溢出标志位(Write-Collision Clear)
245 val = reg_read_8(spi_base + LS1C_SPI_SPSR_OFFSET);
246 if (LS1C_SPI_SPSR_WCOL_MASK & val)
247 {
248 printf("[%s] clear register SPSR's wcol!\r\n", __FUNCTION__); // 手册和linux源码中不一样,加个打印看看
249 reg_write_8(val & ~LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET); // 写0,linux源码中是写0
250 // reg_write_8(val | LS1C_SPI_SPSR_WCOL_MASK, spi_base + LS1C_SPI_SPSR_OFFSET); // 写1,按照1c手册,应该写1
251 }
252
253 return ;
254 }
255
256
257
258 /*
259 * 通过指定SPI发送接收一个字节
260 * 注意,在多任务的系统中,此函数需要互斥。
261 * 即保证在和某个从设备收发某个字节的过程中,不能被切换到其它任务同时与另外的在同一个SPI总线上的从设备通信
262 * 因为龙芯1c的每路SPI上可能接有不同的从设备,通信频率、模式等可能不同
263 * @spi_base 基地址
264 * @tx_ch 待发送的数据
265 * @ret 收到的数据
266 */
ls1c_spi_txrx_byte(void * spi_base,unsigned char tx_ch)267 unsigned char ls1c_spi_txrx_byte(void *spi_base, unsigned char tx_ch)
268 {
269 unsigned char rx_ch = 0;
270
271 // 收发数据
272 reg_write_8(tx_ch, spi_base + LS1C_SPI_TxFIFO_OFFSET); // 开始发送
273 ls1c_spi_wait_txrx_done(spi_base); // 等待收发完成
274 rx_ch = reg_read_8(spi_base + LS1C_SPI_RxFIFO_OFFSET); // 读取收到的数据
275 ls1c_spi_clear(spi_base); // 清中断和标志位
276
277 return rx_ch;
278 }
279
280
281
282