1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-07-27   Chushicheng    the first version
9  */
10 
11 #include "drv_spi.h"
12 #include "pico/binary_info.h"
13 #include "hardware/gpio.h"
14 #include "hardware/spi.h"
15 #include "hardware/dma.h"
16 
17 #ifdef BSP_USING_SPI
18 #define DBG_TAG              "drv.spi"
19 #define DBG_LVL               DBG_INFO
20 #include <rtdbg.h>
21 
22 struct pico_spi
23 {
24     struct rt_spi_bus parent;
25     spi_inst_t *handle;
26     rt_uint8_t spi_rx_pin;
27     rt_uint8_t spi_tx_pin;
28     rt_uint8_t spi_sck_pin;
29     rt_uint8_t spi_cs_pin;
30     rt_uint8_t dma_tx;
31     rt_uint8_t dma_rx;
32     char *device_name;
33 
34 };
35 
36 static struct pico_spi pico_spi_obj[] =
37 {
38 #ifdef BSP_USING_SPI0
39     {
40         .handle = spi0,
41         .spi_rx_pin  = BSP_SPI0_MISO_PIN,
42         .spi_tx_pin  = BSP_SPI0_MOSI_PIN,
43         .spi_sck_pin = BSP_SPI0_SCK_PIN,
44         .spi_cs_pin  = BSP_SPI0_CS_PIN,
45         .device_name = "spi0",
46     },
47 #endif
48 #ifdef BSP_USING_SPI1
49     {
50         .handle = spi1,
51         .spi_rx_pin  = BSP_SPI1_MISO_PIN,
52         .spi_tx_pin  = BSP_SPI1_MOSI_PIN,
53         .spi_sck_pin = BSP_SPI1_SCK_PIN,
54         .spi_cs_pin  = BSP_SPI1_CS_PIN,
55         .device_name = "spi1",
56     },
57 #endif
58 };
59 
pico_spi_init(struct pico_spi * spi_drv,struct rt_spi_configuration * cfg)60 static rt_err_t pico_spi_init(struct pico_spi *spi_drv, struct rt_spi_configuration *cfg)
61 {
62     RT_ASSERT(spi_drv != RT_NULL);
63     RT_ASSERT(cfg != RT_NULL);
64 
65     spi_inst_t *spi_handle = spi_drv->handle;
66     spi_cpol_t  cpol;
67     spi_cpha_t  cpha;
68     rt_uint8_t  dma_transfer_size;
69 
70     if (cfg->mode & RT_SPI_SLAVE)
71     {
72         spi_set_slave(spi_handle, true);
73     }
74     else
75     {
76         spi_set_slave(spi_handle, false);
77     }
78 
79     if (cfg->mode & RT_SPI_CPHA)
80     {
81         cpha = SPI_CPHA_1;
82     }
83     else
84     {
85         cpha = SPI_CPHA_0;
86     }
87 
88     if (cfg->mode & RT_SPI_CPOL)
89     {
90         cpol = SPI_CPOL_1;
91     }
92     else
93     {
94         cpol = SPI_CPOL_0;
95     }
96 
97     if (cfg->data_width >= 4
98         && cfg->data_width <= 16)
99     {
100         spi_set_format(spi_handle, cfg->data_width, cpol, cpha, SPI_MSB_FIRST);
101     }
102     else
103     {
104         return -RT_EIO;
105     }
106 
107     LOG_D("spi baudrate:%d", cfg->max_hz);
108     spi_init(spi_handle, cfg->max_hz);
109     gpio_set_function(spi_drv->spi_rx_pin, GPIO_FUNC_SPI);
110     gpio_init(spi_drv->spi_cs_pin);
111     gpio_set_function(spi_drv->spi_sck_pin, GPIO_FUNC_SPI);
112     gpio_set_function(spi_drv->spi_tx_pin, GPIO_FUNC_SPI);
113     // Make the SPI pins available to picotool
114     bi_decl(bi_3pins_with_func(spi_drv->spi_rx_pin, spi_drv->spi_tx_pin, spi_drv->spi_sck_pin, GPIO_FUNC_SPI));
115     // Make the CS pin available to picotool
116     bi_decl(bi_1pin_with_name(spi_drv->spi_cs_pin, "SPI CS"));
117 
118     // Grab some unused dma channels
119     spi_drv->dma_tx = dma_claim_unused_channel(true);
120     spi_drv->dma_rx = dma_claim_unused_channel(true);
121     /* DMA configuration */
122     if(cfg->data_width == 8)
123     {
124         dma_transfer_size = DMA_SIZE_8;
125     }
126     else if(cfg->data_width == 16)
127     {
128         dma_transfer_size = DMA_SIZE_16;
129     }
130     else if(cfg->data_width == 32)
131     {
132         dma_transfer_size = DMA_SIZE_32;
133     }
134     dma_channel_config c = dma_channel_get_default_config(spi_drv->dma_tx);
135     channel_config_set_transfer_data_size(&c, dma_transfer_size);
136     channel_config_set_dreq(&c, spi_get_index(spi_handle) ? DREQ_SPI1_TX : DREQ_SPI0_TX);
137     dma_channel_set_config(spi_drv->dma_tx, &c, false);
138 
139     c = dma_channel_get_default_config(spi_drv->dma_rx);
140     channel_config_set_transfer_data_size(&c, dma_transfer_size);
141     channel_config_set_dreq(&c, spi_get_index(spi_handle) ? DREQ_SPI1_RX : DREQ_SPI0_RX);
142     channel_config_set_read_increment(&c, false);
143     channel_config_set_write_increment(&c, true);
144     dma_channel_set_config(spi_drv->dma_rx, &c, false);
145     return RT_EOK;
146 }
147 
rt_hw_spi_device_attach(const char * bus_name,const char * device_name,rt_base_t cs_pin)148 rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin)
149 {
150     rt_err_t ret = RT_EOK;
151 
152     struct rt_spi_device *spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
153 
154     ret = rt_spi_bus_attach_device_cspin(spi_device, device_name, bus_name, cs_pin, RT_NULL);
155 
156     return ret;
157 }
158 
spixfer(struct rt_spi_device * device,struct rt_spi_message * message)159 static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *message)
160 {
161     int i;
162 
163     RT_ASSERT(device != RT_NULL);
164     RT_ASSERT(device->bus != RT_NULL);
165 
166     struct pico_spi *spi = rt_container_of(device->bus, struct pico_spi, parent);
167 
168     if(message->cs_take && (device->cs_pin != PIN_NONE))
169     {
170         rt_pin_write(device->cs_pin, PIN_LOW);
171     }
172 
173     dma_channel_config c = dma_get_channel_config(spi->dma_tx);
174     dma_channel_configure(spi->dma_tx, &c,
175                           &spi_get_hw(spi->handle)->dr, // write address
176                           (uint8_t *)(message->send_buf), // read address
177                           message->length, // element count (each element is of size transfer_data_size)
178                           false); // don't start yet
179 
180     c = dma_get_channel_config(spi->dma_rx);
181     dma_channel_configure(spi->dma_rx, &c,
182                           (uint8_t *)(message->recv_buf), // write address
183                           &spi_get_hw(spi->handle)->dr, // read address
184                           message->length, // element count (each element is of size transfer_data_size)
185                           false); // don't start yet
186 
187     dma_start_channel_mask((1u << spi->dma_tx) | (1u << spi->dma_rx));
188     dma_channel_wait_for_finish_blocking(spi->dma_tx);
189     dma_channel_wait_for_finish_blocking(spi->dma_rx);
190 
191     if(message->cs_release && (device->cs_pin != PIN_NONE))
192     {
193         rt_pin_write(device->cs_pin, PIN_HIGH);
194     }
195 
196     return message->length;
197 }
198 
spi_configure(struct rt_spi_device * device,struct rt_spi_configuration * configuration)199 static rt_err_t spi_configure(struct rt_spi_device *device,
200                               struct rt_spi_configuration *configuration)
201 {
202     rt_err_t ret = RT_EOK;
203     struct pico_spi *spi = rt_container_of(device->bus, struct pico_spi, parent);
204     ret = pico_spi_init(spi, configuration);
205 
206     return ret;
207 }
208 
209 static const struct rt_spi_ops pico_spi_ops =
210 {
211     .configure = spi_configure,
212     .xfer = spixfer,
213 };
214 
rt_hw_spi_init(void)215 int rt_hw_spi_init(void)
216 {
217     int result = RT_EOK;
218 
219     for (rt_size_t i = 0; i < sizeof(pico_spi_obj) / sizeof(struct pico_spi); i++)
220     {
221         LOG_D("%s initing", pico_spi_obj[i].device_name);
222         /* register spi device */
223         if (rt_spi_bus_register(&pico_spi_obj[i].parent, pico_spi_obj[i].device_name, &pico_spi_ops) == RT_EOK)
224         {
225             LOG_D("%s init success", pico_spi_obj[i].device_name);
226         }
227         else
228         {
229             LOG_E("%s register failed", pico_spi_obj[i].device_name);
230             result = -RT_ERROR;
231         }
232     }
233 
234     return result;
235 }
236 INIT_BOARD_EXPORT(rt_hw_spi_init);
237 
238 #endif /* BSP_USING_SPI */
239