1 /*
2  * Copyright (c) 2006-2022, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2017-06-05     tanek        first implementation.
9  * 2018-04-19     misonyo      Porting for v85xxf30x
10  * 2019-03-31     xuzhuoyi     Porting for v85xxe230
11  * 2021-09-21     zhuxw        Porting for v85xx
12  */
13 
14 #include "drv_spi.h"
15 #include "board.h"
16 #include <rtthread.h>
17 
18 #if defined(RT_USING_SPI) && defined(RT_USING_PIN)
19 #include <rtdevice.h>
20 
21 #if !defined(RT_USING_SPI1) && !defined(RT_USING_SPI2)
22 #error "Please define at least one SPIx"
23 #endif
24 
25 /* private rt-thread spi ops function */
26 static rt_err_t configure(struct rt_spi_device* device, struct rt_spi_configuration* configuration);
27 static rt_uint32_t xfer(struct rt_spi_device* device, struct rt_spi_message* message);
28 
29 static struct rt_spi_ops v85xx_spi_ops =
30 {
31     configure,
32     xfer
33 };
34 
configure(struct rt_spi_device * device,struct rt_spi_configuration * configuration)35 static rt_err_t configure(struct rt_spi_device* device, struct rt_spi_configuration* configuration)
36 {
37     SPI_InitType spi_init_struct;
38 
39     rt_uint32_t spi_periph = (rt_uint32_t)device->bus->parent.user_data;
40 
41     RT_ASSERT(device != RT_NULL);
42     RT_ASSERT(configuration != RT_NULL);
43 
44     if(configuration->data_width > 8)
45     {
46         return -RT_EIO;
47     }
48 
49     {
50         rt_uint32_t spi_apb_clock;
51         rt_uint32_t max_hz;
52 
53         max_hz = configuration->max_hz;
54 
55         spi_apb_clock = CLK_GetPCLKFreq();
56 
57         if(max_hz >= spi_apb_clock/2)
58         {
59             spi_init_struct.ClockDivision = SPI_CLKDIV_2;
60         }
61         else if (max_hz >= spi_apb_clock/4)
62         {
63             spi_init_struct.ClockDivision = SPI_CLKDIV_4;
64         }
65         else if (max_hz >= spi_apb_clock/8)
66         {
67             spi_init_struct.ClockDivision = SPI_CLKDIV_8;
68         }
69         else if (max_hz >= spi_apb_clock/16)
70         {
71             spi_init_struct.ClockDivision = SPI_CLKDIV_16;
72         }
73         else if (max_hz >= spi_apb_clock/32)
74         {
75             spi_init_struct.ClockDivision = SPI_CLKDIV_32;
76         }
77         else if (max_hz >= spi_apb_clock/64)
78         {
79             spi_init_struct.ClockDivision = SPI_CLKDIV_64;
80         }
81         else
82         {
83             /*  min prescaler 128 */
84             spi_init_struct.ClockDivision = SPI_CLKDIV_128;
85         }
86     } /* baudrate */
87 
88     switch(configuration->mode & RT_SPI_MODE_3)
89     {
90     case RT_SPI_MODE_0:
91         spi_init_struct.SPH = SPI_SPH_0;
92         spi_init_struct.SPO = SPI_SPO_0;
93         break;
94     case RT_SPI_MODE_1:
95         spi_init_struct.SPH = SPI_SPH_1;
96         spi_init_struct.SPO = SPI_SPO_0;
97         break;
98     case RT_SPI_MODE_2:
99         spi_init_struct.SPH = SPI_SPH_0;
100         spi_init_struct.SPO = SPI_SPO_1;
101         break;
102     case RT_SPI_MODE_3:
103         spi_init_struct.SPH = SPI_SPH_1;
104         spi_init_struct.SPO = SPI_SPO_1;
105         break;
106     }
107 
108     if(!(configuration->mode & RT_SPI_MSB))
109     {
110         return -RT_EIO;
111     }
112 
113     spi_init_struct.Mode = SPI_MODE_MASTER;
114     spi_init_struct.CSNSoft = SPI_CSNSOFT_ENABLE;
115 
116     SPI_Init((SPI_TypeDef*)spi_periph, &spi_init_struct);
117 
118     SPI_Cmd((SPI_TypeDef*)spi_periph, ENABLE);
119 
120     return RT_EOK;
121 };
122 
xfer(struct rt_spi_device * device,struct rt_spi_message * message)123 static rt_uint32_t xfer(struct rt_spi_device* device, struct rt_spi_message* message)
124 {
125     rt_base_t v85xx_cs_pin = (rt_base_t)device->parent.user_data;
126     rt_uint32_t spi_periph = (rt_uint32_t)device->bus->parent.user_data;
127     struct rt_spi_configuration * config = &device->config;
128 
129     RT_ASSERT(device != RT_NULL);
130     RT_ASSERT(message != RT_NULL);
131 
132     /* take CS */
133     if(message->cs_take)
134     {
135         rt_pin_write(v85xx_cs_pin, PIN_LOW);
136         DEBUG_PRINTF("spi take cs\n");
137     }
138 
139     {
140         if(config->data_width <= 8)
141         {
142             const rt_uint8_t * send_ptr = message->send_buf;
143             rt_uint8_t * recv_ptr = message->recv_buf;
144             rt_uint32_t size = message->length;
145 
146             DEBUG_PRINTF("spi poll transfer start: %d\n", size);
147 
148             while(size--)
149             {
150                 rt_uint8_t data = 0xFF;
151 
152                 if(send_ptr != RT_NULL)
153                 {
154                     data = *send_ptr++;
155                 }
156 
157                 //Wait until the transmit buffer is empty
158                 while(RESET == SPI_GetStatus((SPI_TypeDef*)spi_periph, SPI_STS_TXEMPTY));
159                 // Send the byte
160                 SPI_SendData((SPI_TypeDef*)spi_periph, data);
161 
162                 //Wait until a data is received
163                 while(RESET == SPI_GetStatus((SPI_TypeDef*)spi_periph, SPI_STS_RNE));
164                 // Get the received data
165                 data = SPI_ReceiveData((SPI_TypeDef*)spi_periph);
166 
167                 if(recv_ptr != RT_NULL)
168                 {
169                     *recv_ptr++ = data;
170                 }
171             }
172             DEBUG_PRINTF("spi poll transfer finsh\n");
173         }
174     }
175 
176     /* release CS */
177     if(message->cs_release)
178     {
179         rt_pin_write(v85xx_cs_pin, PIN_HIGH);
180         DEBUG_PRINTF("spi release cs\n");
181     }
182 
183     return message->length;
184 };
185 
v85xx_hw_spi_init(void)186 int v85xx_hw_spi_init(void)
187 {
188     int result = 0;
189 #ifdef RT_USING_SPI1
190     static struct rt_spi_bus spi_bus0;
191     spi_bus0.parent.user_data = (void *)SPI1;
192 
193     result = rt_spi_bus_register(&spi_bus0, "spi1", &v85xx_spi_ops);
194 
195 #endif
196 
197 #ifdef RT_USING_SPI2
198     static struct rt_spi_bus spi_bus1;
199     spi_bus1.parent.user_data = (void *)SPI2;
200 
201     result = rt_spi_bus_register(&spi_bus1, "spi2", &v85xx_spi_ops);
202 
203 #endif
204     return result;
205 }
206 INIT_BOARD_EXPORT(v85xx_hw_spi_init);
207 #endif
208