1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author                   Notes
8  * 2020-10-28     0xcccccccccccc           Initial Version
9  * 2021-01-17     0xcccccccccccc           Bug Fixed : clock division cannot been adjusted as expected due to wrong register configuration.
10  */
11 
12 /**
13  * @addtogroup ls2k
14  */
15 /*@{*/
16 
17 #include <stdlib.h>
18 #include <ctype.h>
19 #include <stdint.h>
20 #include <rtthread.h>
21 #include "drivers/dev_spi.h"
22 #include "drv_spi.h"
23 
24 #ifdef RT_USING_SPI
25 #ifdef RT_USING_SPI_GPIOCS
26 #include <drivers/dev_pin.h>
27 #endif
28 
spi_init(uint8_t spre_spr,uint8_t copl,uint8_t cpha)29 static void spi_init(uint8_t spre_spr, uint8_t copl, uint8_t cpha)
30 {
31     SET_SPI(SPSR, 0xc0);
32     SET_SPI(PARAM, 0x40);
33     SET_SPI(PARAM2, 0x01);
34     SET_SPI(SPER, (spre_spr & 0b00001100) >> 2);
35     SET_SPI(SPCR, 0x50 | copl << 3 | cpha << 2 | (spre_spr & 0b00000011));
36     SET_SPI(SOFTCS, 0xff);
37 }
38 
spi_set_csn(uint8_t val)39 rt_inline void spi_set_csn(uint8_t val)
40 {
41     SET_SPI(SOFTCS, val);
42 }
43 
spi_set_cs(unsigned char cs,int new_status)44 static void spi_set_cs(unsigned char cs, int new_status)
45 {
46     if (cs < 4)
47     {
48         unsigned char val = 0;
49         val = GET_SPI(SOFTCS);
50         val |= 0x01 << cs ;     // csen=1
51         if (new_status)         // cs = 1
52         {
53             val |= (0x10 << cs);            // csn=1
54         }
55         else                    // cs = 0
56         {
57             val &= ~(0x10 << cs);           // csn=0
58         }
59         SET_SPI(SOFTCS, val);
60         return ;
61     }
62 #ifdef RT_USING_SPI_GPIOCS
63     else
64     {
65         rt_pin_mode(cs, PIN_MODE_OUTPUT); // with RT_USING_SPI_GPIOCS feature enabled, gpio will be used as csn pin.
66         rt_pin_write(cs, new_status);
67     }
68 #endif
69 }
70 
spi_write_for_response(uint8_t data)71 static uint8_t spi_write_for_response(uint8_t data)
72 {
73     uint8_t val;
74     SET_SPI(TXFIFO, data);
75     while ((GET_SPI(SPSR))&RFEMPTY); //wait for echo
76     val = GET_SPI(RXFIFO);
77     return val;
78 }
79 
cmd_spi_init(int argc,char * argv[])80 static int cmd_spi_init(int argc, char *argv[])
81 {
82     uint8_t spre_spr, cpol, cpha;
83     switch (argc)
84     {
85     case 2:
86         spre_spr = strtoul(argv[1], NULL, 0);
87         spi_init(spre_spr, 0, 0);
88         break;
89     case 4:
90         spre_spr = strtoul(argv[1], NULL, 0);
91         cpol = strtoul(argv[2], NULL, 0);
92         cpha = strtoul(argv[3], NULL, 0);
93         spi_init(spre_spr, 0, 0);
94         break;
95     default:
96         printf("\nusage : cmd_spi_init spre_spr <cpol> <cpha>\n(cmd_spi_init 0x4 0x0 0x0)\n0x4:div8 0xb:div4096\n");
97         break;
98     }
99 }
100 MSH_CMD_EXPORT(cmd_spi_init, cmd_spi_init);
101 
cmd_spi_set_csn(int argc,char * argv[])102 static int cmd_spi_set_csn(int argc, char *argv[])
103 {
104     uint8_t val, csn;
105     switch (argc)
106     {
107     case 3:
108         csn = strtoul(argv[1], NULL, 0);
109         val = strtoul(argv[2], NULL, 0);
110         spi_set_cs(csn, val);
111         break;
112     default:
113         printf("usage:cmd_spi_set_csn csn val\n(0xbf for csn1 enable,0xff for csn1 disable)\n");
114         break;
115     }
116 }
117 MSH_CMD_EXPORT(cmd_spi_set_csn, cmd_spi_set_csn);
118 
cmd_spi_write(int argc,char * argv[])119 static int cmd_spi_write(int argc, char *argv[])
120 {
121     uint8_t data, resp;
122     switch (argc)
123     {
124     case 2:
125         data = strtoul(argv[1], NULL, 0);
126         resp = spi_write_for_response(data);
127         printf("resp:%2X\n", resp);
128         break;
129     default:
130         printf("usage:cmd_spi_write data\n");
131         break;
132     }
133 }
134 MSH_CMD_EXPORT(cmd_spi_write, cmd_spi_write);
135 
136 static rt_err_t configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration);
137 static rt_uint32_t xfer(struct rt_spi_device *device, struct rt_spi_message *message);
138 
139 const static unsigned char SPI_DIV_TABLE[] = {0b0000, 0b0001, 0b0100, 0b0010, 0b0011, 0b0101, 0b0110, 0b0111, 0b1000, 0b1001, 0b1010, 0b1011};
140 // 2      4      8      16     32     64      128   256    512    1024   2048   4096
configure(struct rt_spi_device * device,struct rt_spi_configuration * configuration)141 static rt_err_t configure(struct rt_spi_device *device,
142                           struct rt_spi_configuration *configuration)
143 {
144 
145     unsigned char cpol = 0;
146     unsigned char cpha = 0;
147 
148     RT_ASSERT(NULL != device);
149     RT_ASSERT(NULL != configuration);
150 
151     // baudrate
152     if (configuration->mode & RT_SPI_CPOL)      // cpol
153     {
154         cpol = 1;
155     }
156     else
157     {
158         cpol = 0;
159     }
160     if (configuration->mode & RT_SPI_CPHA)      // cpha
161     {
162         cpha = 1;
163     }
164     else
165     {
166         cpha = 0;
167     }
168 
169     float spi_max_speed = ((float)APB_MAX_SPEED) / (8.0 / (float)APB_FREQSCALE);
170     uint64_t div = (uint64_t)(spi_max_speed / (float)configuration->max_hz);
171     int ctr = 0;
172     while (div != 1 && ctr < 12)
173     {
174         ctr++;
175         div = div >> 1;
176     }
177     spi_init(SPI_DIV_TABLE[ctr], cpol, cpha);
178 
179     return RT_EOK;
180 }
181 
xfer(struct rt_spi_device * device,struct rt_spi_message * message)182 static rt_uint32_t xfer(struct rt_spi_device *device, struct rt_spi_message *message)
183 {
184 
185     unsigned char cs = 0;
186     rt_uint32_t size = 0;
187     const rt_uint8_t *send_ptr = NULL;
188     rt_uint8_t *recv_ptr = NULL;
189     rt_uint8_t data = 0;
190     RT_ASSERT(NULL != device);
191     RT_ASSERT(NULL != message);
192     cs = (unsigned char)(device->parent.user_data);
193     size = message->length;
194     if (message->cs_take)
195     {
196         spi_set_cs(cs, 0);
197     }
198     // send data
199     send_ptr = message->send_buf;
200     recv_ptr = message->recv_buf;
201     while (size--)
202     {
203         data = 0xFF;
204         if (NULL != send_ptr)
205         {
206             data = *send_ptr++;
207         }
208         if (NULL != recv_ptr)
209         {
210             *recv_ptr++ = spi_write_for_response(data);
211         }
212         else
213         {
214             spi_write_for_response(data);
215         }
216     }
217     // release cs
218     if (message->cs_release)
219     {
220         spi_set_cs(cs, 1);
221     }
222     return message->length;
223 }
224 
225 static struct rt_spi_ops loongson_spi_ops =
226 {
227     .configure  = configure,
228     .xfer       = xfer
229 };
230 static struct rt_spi_bus loongson_spi;
231 
loongson_spi_init()232 static int loongson_spi_init()
233 {
234     //rt_kprintf("spi_init\n");
235     return rt_spi_bus_register(&loongson_spi, "spi", &loongson_spi_ops);
236 }
237 INIT_BOARD_EXPORT(loongson_spi_init);
238 #endif
239 /*@}*/
240