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