1 /*
2  * Copyright (c) 2006-2022, Synwit Technology Co.,Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2018-05-31     ZYH          first version
9  * 2018-12-10     Zohar_Lee    format file
10  * 2020-07-10     lik          format file
11  */
12 
13 #include "drv_spi.h"
14 
15 #ifdef RT_USING_SPI
16 #ifdef BSP_USING_SPI
17 
18 //#define DRV_DEBUG
19 #define LOG_TAG "drv.spi"
20 #include <drv_log.h>
21 
22 #if !defined(BSP_USING_SPI0) && !defined(BSP_USING_SPI1)
23 #error "Please define at least one BSP_USING_SPIx"
24 /* this driver can be disabled at menuconfig ? RT-Thread Components ? Device Drivers */
25 #endif
26 
27 struct swm_spi_cs
28 {
29     GPIO_TypeDef *GPIOx;
30     uint32_t gpio_pin;
31 };
32 
33 struct swm_spi_cfg
34 {
35     const char *name;
36     SPI_TypeDef *SPIx;
37     SPI_InitStructure spi_initstruct;
38 };
39 
40 /* swm spi dirver class */
41 struct swm_spi_device
42 {
43     struct swm_spi_cfg *spi_cfg;
44     struct rt_spi_configuration *configure;
45     struct rt_spi_bus spi_bus;
46 };
47 
48 #ifdef BSP_USING_SPI0
49 #ifndef SPI0_BUS_CONFIG
50 #define SPI0_BUS_CONFIG                               \
51     {                                                 \
52         .name = "spi0",                               \
53         .SPIx = SPI0,                                 \
54         .spi_initstruct.clkDiv = SPI_CLKDIV_32,       \
55         .spi_initstruct.FrameFormat = SPI_FORMAT_SPI, \
56         .spi_initstruct.SampleEdge = SPI_SECOND_EDGE, \
57         .spi_initstruct.IdleLevel = SPI_HIGH_LEVEL,   \
58         .spi_initstruct.WordSize = 8,                 \
59         .spi_initstruct.Master = 1,                   \
60         .spi_initstruct.RXHFullIEn = 0,               \
61         .spi_initstruct.TXEmptyIEn = 0,               \
62         .spi_initstruct.TXCompleteIEn = 0,            \
63     }
64 #endif /* SPI0_BUS_CONFIG */
65 #endif /* BSP_USING_SPI0 */
66 
67 #ifdef BSP_USING_SPI1
68 #ifndef SPI1_BUS_CONFIG
69 #define SPI1_BUS_CONFIG                               \
70     {                                                 \
71         .name = "spi1",                               \
72         .SPIx = SPI1,                                 \
73         .spi_initstruct.clkDiv = SPI_CLKDIV_32,       \
74         .spi_initstruct.FrameFormat = SPI_FORMAT_SPI, \
75         .spi_initstruct.SampleEdge = SPI_SECOND_EDGE, \
76         .spi_initstruct.IdleLevel = SPI_HIGH_LEVEL,   \
77         .spi_initstruct.WordSize = 8,                 \
78         .spi_initstruct.Master = 1,                   \
79         .spi_initstruct.RXHFullIEn = 0,               \
80         .spi_initstruct.TXEmptyIEn = 0,               \
81         .spi_initstruct.TXCompleteIEn = 0,            \
82     }
83 #endif /* SPI1_BUS_CONFIG */
84 #endif /* BSP_USING_SPI1 */
85 
86 static struct swm_spi_cfg swm_spi_cfg[] =
87     {
88 #ifdef BSP_USING_SPI0
89         SPI0_BUS_CONFIG,
90 #endif
91 #ifdef BSP_USING_SPI1
92         SPI1_BUS_CONFIG,
93 #endif
94 };
95 
96 static struct swm_spi_device spi_bus_obj[sizeof(swm_spi_cfg) / sizeof(swm_spi_cfg[0])] = {0};
97 
swm_spi_configure(struct rt_spi_device * device,struct rt_spi_configuration * configure)98 static rt_err_t swm_spi_configure(struct rt_spi_device *device,
99                                   struct rt_spi_configuration *configure)
100 {
101     RT_ASSERT(device != RT_NULL);
102     RT_ASSERT(configure != RT_NULL);
103 
104     struct swm_spi_device *spi_drv = rt_container_of(device->bus, struct swm_spi_device, spi_bus);
105     spi_drv->configure = configure;
106     struct swm_spi_cfg *spi_cfg = spi_drv->spi_cfg;
107 
108     if (configure->mode & RT_SPI_SLAVE)
109     {
110         spi_cfg->spi_initstruct.Master = 0;
111     }
112     else
113     {
114         spi_cfg->spi_initstruct.Master = 1;
115     }
116 
117     if (configure->mode & RT_SPI_3WIRE)
118     {
119         return -RT_EINVAL;
120     }
121 
122     if (configure->data_width == 8)
123     {
124         spi_cfg->spi_initstruct.WordSize = 8;
125     }
126     else if (configure->data_width == 16)
127     {
128         spi_cfg->spi_initstruct.WordSize = 16;
129     }
130     else
131     {
132         return -RT_EIO;
133     }
134 
135     if (configure->mode & RT_SPI_CPHA)
136     {
137         spi_cfg->spi_initstruct.SampleEdge = SPI_SECOND_EDGE;
138     }
139     else
140     {
141         spi_cfg->spi_initstruct.SampleEdge = SPI_FIRST_EDGE;
142     }
143 
144     if (configure->mode & RT_SPI_CPOL)
145     {
146         spi_cfg->spi_initstruct.IdleLevel = SPI_HIGH_LEVEL;
147     }
148     else
149     {
150         spi_cfg->spi_initstruct.IdleLevel = SPI_LOW_LEVEL;
151     }
152 
153     if (configure->max_hz >= SystemCoreClock / 4)
154     {
155         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_4;
156     }
157     else if (configure->max_hz >= SystemCoreClock / 8)
158     {
159         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_8;
160     }
161     else if (configure->max_hz >= SystemCoreClock / 16)
162     {
163         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_16;
164     }
165     else if (configure->max_hz >= SystemCoreClock / 32)
166     {
167         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_32;
168     }
169     else if (configure->max_hz >= SystemCoreClock / 64)
170     {
171         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_64;
172     }
173     else if (configure->max_hz >= SystemCoreClock / 128)
174     {
175         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_128;
176     }
177     else if (configure->max_hz >= SystemCoreClock / 256)
178     {
179         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_256;
180     }
181     else
182     {
183         /*  min prescaler 512 */
184         spi_cfg->spi_initstruct.clkDiv = SPI_CLKDIV_512;
185     }
186 
187     SPI_Init(spi_cfg->SPIx, &(spi_cfg->spi_initstruct));
188     SPI_Open(spi_cfg->SPIx);
189     LOG_D("%s init done", spi_cfg->name);
190     return RT_EOK;
191 }
192 
193 #define SPISTEP(datalen) (((datalen) == 8) ? 1 : 2)
194 #define SPISEND_1(reg, ptr, datalen)       \
195     do                                     \
196     {                                      \
197         if (datalen == 8)                  \
198         {                                  \
199             (reg) = *(rt_uint8_t *)(ptr);  \
200         }                                  \
201         else                               \
202         {                                  \
203             (reg) = *(rt_uint16_t *)(ptr); \
204         }                                  \
205     } while (0)
206 #define SPIRECV_1(reg, ptr, datalen)      \
207     do                                    \
208     {                                     \
209         if (datalen == 8)                 \
210         {                                 \
211             *(rt_uint8_t *)(ptr) = (reg); \
212         }                                 \
213         else                              \
214         {                                 \
215             *(rt_uint16_t *)(ptr) = reg;  \
216         }                                 \
217     } while (0)
218 
swm_spi_txrx1b(struct swm_spi_device * spi_drv,void * rcvb,const void * sndb)219 static rt_err_t swm_spi_txrx1b(struct swm_spi_device *spi_drv, void *rcvb, const void *sndb)
220 {
221     rt_uint32_t padrcv = 0;
222     rt_uint32_t padsnd = 0xFF;
223     if (!rcvb && !sndb)
224     {
225         return -RT_ERROR;
226     }
227     if (!rcvb)
228     {
229         rcvb = &padrcv;
230     }
231     if (!sndb)
232     {
233         sndb = &padsnd;
234     }
235     while (SPI_IsTXFull(spi_drv->spi_cfg->SPIx))
236         ;
237     SPISEND_1(spi_drv->spi_cfg->SPIx->DATA, sndb, spi_drv->spi_cfg->spi_initstruct.WordSize);
238     while (SPI_IsRXEmpty(spi_drv->spi_cfg->SPIx))
239         ;
240     SPIRECV_1(spi_drv->spi_cfg->SPIx->DATA, rcvb, spi_drv->spi_cfg->spi_initstruct.WordSize);
241     return RT_EOK;
242 }
243 
swm_spi_xfer(struct rt_spi_device * device,struct rt_spi_message * message)244 static rt_uint32_t swm_spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
245 {
246     rt_err_t res;
247 
248     RT_ASSERT(device != RT_NULL);
249     RT_ASSERT(device->bus != RT_NULL);
250     RT_ASSERT(device->bus->parent.user_data != RT_NULL);
251     RT_ASSERT(message != RT_NULL);
252 
253     struct swm_spi_device *spi_drv = rt_container_of(device->bus, struct swm_spi_device, spi_bus);
254     struct swm_spi_cfg *spi_cfg = spi_drv->spi_cfg;
255     struct swm_spi_cs *cs = device->parent.user_data;
256 
257     if (message->cs_take)
258     {
259         GPIO_ClrBit(cs->GPIOx, cs->gpio_pin);
260     }
261 
262     LOG_D("%s transfer prepare and start", spi_cfg->name);
263     LOG_D("%s sendbuf: %X, recvbuf: %X, length: %d",
264           spi_cfg->name, (uint32_t)message->send_buf, (uint32_t)message->recv_buf, message->length);
265 
266     const rt_uint8_t *sndb = message->send_buf;
267     rt_uint8_t *rcvb = message->recv_buf;
268     rt_int32_t length = message->length;
269 
270     while (length)
271     {
272         res = swm_spi_txrx1b(spi_drv, rcvb, sndb);
273         if (rcvb)
274         {
275             rcvb += SPISTEP(spi_cfg->spi_initstruct.WordSize);
276         }
277         if (sndb)
278         {
279             sndb += SPISTEP(spi_cfg->spi_initstruct.WordSize);
280         }
281         if (res != RT_EOK)
282         {
283             break;
284         }
285         length--;
286     }
287     /* Wait until Busy flag is reset before disabling SPI */
288     while (!SPI_IsTXEmpty(spi_cfg->SPIx) && !SPI_IsRXEmpty(spi_cfg->SPIx))
289         ;
290     if (message->cs_release)
291     {
292         GPIO_SetBit(cs->GPIOx, cs->gpio_pin);
293     }
294     return message->length - length;
295 }
296 
297 const static struct rt_spi_ops swm_spi_ops =
298     {
299         .configure = swm_spi_configure,
300         .xfer = swm_spi_xfer,
301 };
302 
303 //cannot be used before completion init
rt_hw_spi_device_attach(const char * bus_name,const char * device_name,GPIO_TypeDef * cs_gpiox,uint32_t cs_gpio_pin)304 rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef *cs_gpiox, uint32_t cs_gpio_pin)
305 {
306     RT_ASSERT(bus_name != RT_NULL);
307     RT_ASSERT(device_name != RT_NULL);
308 
309     rt_err_t result;
310     struct rt_spi_device *spi_device;
311     struct swm_spi_cs *cs_pin;
312 
313     GPIO_Init(cs_gpiox, cs_gpio_pin, 1, 0, 0);
314     GPIO_SetBit(cs_gpiox, cs_gpio_pin);
315 
316     spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
317     RT_ASSERT(spi_device != RT_NULL);
318     cs_pin = (struct swm_spi_cs *)rt_malloc(sizeof(struct swm_spi_cs));
319     RT_ASSERT(cs_pin != RT_NULL);
320     cs_pin->GPIOx = cs_gpiox;
321     cs_pin->gpio_pin = cs_gpio_pin;
322 
323     result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);
324     if (result != RT_EOK)
325     {
326         LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
327     }
328     RT_ASSERT(result == RT_EOK);
329     LOG_D("%s attach to %s done", device_name, bus_name);
330     return result;
331 }
332 
swm_spi_init(void)333 int swm_spi_init(void)
334 {
335     rt_err_t result;
336 
337 #ifdef BSP_USING_SPI0
338     PORT_Init(PORTP, PIN23, FUNMUX1_SPI0_SCLK, 0);
339     PORT_Init(PORTP, PIN18, FUNMUX0_SPI0_MOSI, 0);
340     PORT_Init(PORTP, PIN19, FUNMUX1_SPI0_MISO, 1);
341 #endif //BSP_USING_SPI0
342 
343 #ifdef BSP_USING_SPI1
344     PORT_Init(PORTB, PIN1, FUNMUX1_SPI1_SCLK, 0);
345     PORT_Init(PORTB, PIN2, FUNMUX0_SPI1_MOSI, 0);
346     PORT_Init(PORTB, PIN3, FUNMUX1_SPI1_MISO, 1);
347 #endif //BSP_USING_SPI1
348     for (int i = 0; i < sizeof(swm_spi_cfg) / sizeof(swm_spi_cfg[0]); i++)
349     {
350         spi_bus_obj[i].spi_cfg = &swm_spi_cfg[i];
351         spi_bus_obj[i].spi_bus.parent.user_data = &swm_spi_cfg[i];
352         result = rt_spi_bus_register(&spi_bus_obj[i].spi_bus, swm_spi_cfg[i].name, &swm_spi_ops);
353         if (result != RT_EOK)
354         {
355             LOG_E("%s bus register fail.", swm_spi_cfg[i].name);
356         }
357         else
358         {
359             LOG_D("%s bus register success.", swm_spi_cfg[i].name);
360         }
361     }
362 
363     return result;
364 }
365 INIT_BOARD_EXPORT(swm_spi_init);
366 
367 #endif /* BSP_USING_SPI */
368 #endif /* RT_USING_SPI */
369