1 /*
2 * Copyright (c) 2021 Marc Reilly - Creative Product Design
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT zephyr_spi_bitbang
8
9 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(spi_bitbang);
12
13 #include <zephyr/sys/sys_io.h>
14 #include <zephyr/drivers/spi.h>
15 #include <zephyr/drivers/spi/rtio.h>
16 #include "spi_context.h"
17
18 struct spi_bitbang_data {
19 struct spi_context ctx;
20 int bits;
21 int wait_us;
22 int dfs;
23 };
24
25 struct spi_bitbang_config {
26 struct gpio_dt_spec clk_gpio;
27 struct gpio_dt_spec mosi_gpio;
28 struct gpio_dt_spec miso_gpio;
29 };
30
spi_bitbang_configure(const struct spi_bitbang_config * info,struct spi_bitbang_data * data,const struct spi_config * config)31 static int spi_bitbang_configure(const struct spi_bitbang_config *info,
32 struct spi_bitbang_data *data,
33 const struct spi_config *config)
34 {
35 if (config->operation & SPI_OP_MODE_SLAVE) {
36 LOG_ERR("Slave mode not supported");
37 return -ENOTSUP;
38 }
39
40 if (config->operation & (SPI_LINES_DUAL | SPI_LINES_QUAD | SPI_LINES_OCTAL)) {
41 LOG_ERR("Unsupported configuration");
42 return -ENOTSUP;
43 }
44
45 const int bits = SPI_WORD_SIZE_GET(config->operation);
46
47 if (bits > 16) {
48 LOG_ERR("Word sizes > 16 bits not supported");
49 return -ENOTSUP;
50 }
51
52 data->bits = bits;
53 data->dfs = ((data->bits - 1) / 8) + 1;
54 if (config->frequency > 0) {
55 /* convert freq to period, the extra /2 is due to waiting
56 * twice in each clock cycle. The '2000' is an upscale factor.
57 */
58 data->wait_us = (1000000ul * 2000ul / config->frequency) / 2000ul;
59 data->wait_us /= 2;
60 } else {
61 data->wait_us = 8 / 2; /* 125 kHz */
62 }
63
64 data->ctx.config = config;
65
66 return 0;
67 }
68
spi_bitbang_transceive(const struct device * dev,const struct spi_config * spi_cfg,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)69 static int spi_bitbang_transceive(const struct device *dev,
70 const struct spi_config *spi_cfg,
71 const struct spi_buf_set *tx_bufs,
72 const struct spi_buf_set *rx_bufs)
73 {
74 const struct spi_bitbang_config *info = dev->config;
75 struct spi_bitbang_data *data = dev->data;
76 struct spi_context *ctx = &data->ctx;
77 int rc;
78 const struct gpio_dt_spec *miso = NULL;
79 const struct gpio_dt_spec *mosi = NULL;
80 gpio_flags_t mosi_flags = GPIO_OUTPUT_INACTIVE;
81
82 rc = spi_bitbang_configure(info, data, spi_cfg);
83 if (rc < 0) {
84 return rc;
85 }
86
87 if (spi_cfg->operation & SPI_HALF_DUPLEX) {
88 if (!info->mosi_gpio.port) {
89 LOG_ERR("No MOSI pin specified in half duplex mode");
90 return -EINVAL;
91 }
92
93 if (tx_bufs && rx_bufs) {
94 LOG_ERR("Both RX and TX specified in half duplex mode");
95 return -EINVAL;
96 } else if (tx_bufs && !rx_bufs) {
97 /* TX mode */
98 mosi = &info->mosi_gpio;
99 } else if (!tx_bufs && rx_bufs) {
100 /* RX mode */
101 mosi_flags = GPIO_INPUT;
102 miso = &info->mosi_gpio;
103 }
104 } else {
105 if (info->mosi_gpio.port) {
106 mosi = &info->mosi_gpio;
107 }
108
109 if (info->miso_gpio.port) {
110 miso = &info->miso_gpio;
111 }
112 }
113
114 if (info->mosi_gpio.port) {
115 rc = gpio_pin_configure_dt(&info->mosi_gpio, mosi_flags);
116 if (rc < 0) {
117 LOG_ERR("Couldn't configure MOSI pin: %d", rc);
118 return rc;
119 }
120 }
121
122 spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs);
123
124 int clock_state = 0;
125 int cpha = 0;
126 bool loop = false;
127 bool lsb = false;
128
129 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) {
130 clock_state = 1;
131 }
132 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) {
133 cpha = 1;
134 }
135 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_LOOP) {
136 loop = true;
137 }
138 if (spi_cfg->operation & SPI_TRANSFER_LSB) {
139 lsb = true;
140 }
141
142 /* set the initial clock state before CS */
143 gpio_pin_set_dt(&info->clk_gpio, clock_state);
144
145 spi_context_cs_control(ctx, true);
146
147 const uint32_t wait_us = data->wait_us;
148
149 while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
150 uint16_t w = 0;
151
152 if (ctx->tx_len) {
153 switch (data->dfs) {
154 case 2:
155 w = *(uint16_t *)(ctx->tx_buf);
156 break;
157 case 1:
158 w = *(uint8_t *)(ctx->tx_buf);
159 break;
160 }
161 }
162
163 uint16_t r = 0;
164 uint8_t i = 0;
165 int b = 0;
166 bool do_read = false;
167
168 if (miso && spi_context_rx_buf_on(ctx)) {
169 do_read = true;
170 }
171
172 while (i < data->bits) {
173 const int shift = lsb ? i : (data->bits - 1 - i);
174 const int d = (w >> shift) & 0x1;
175
176 b = 0;
177
178 /* setup data out first thing */
179 if (mosi) {
180 gpio_pin_set_dt(mosi, d);
181 }
182
183 k_busy_wait(wait_us);
184
185 if (!loop && do_read && !cpha) {
186 b = gpio_pin_get_dt(miso);
187 }
188
189 /* first (leading) clock edge */
190 gpio_pin_set_dt(&info->clk_gpio, !clock_state);
191
192 k_busy_wait(wait_us);
193
194 if (!loop && do_read && cpha) {
195 b = gpio_pin_get_dt(miso);
196 }
197
198 /* second (trailing) clock edge */
199 gpio_pin_set_dt(&info->clk_gpio, clock_state);
200
201 if (loop) {
202 b = d;
203 }
204
205 r |= (b ? 0x1 : 0x0) << shift;
206
207 ++i;
208 }
209
210 if (spi_context_rx_buf_on(ctx)) {
211 switch (data->dfs) {
212 case 2:
213 *(uint16_t *)(ctx->rx_buf) = r;
214 break;
215 case 1:
216 *(uint8_t *)(ctx->rx_buf) = r;
217 break;
218 }
219 }
220
221 LOG_DBG(" w: %04x, r: %04x , do_read: %d", w, r, do_read);
222
223 spi_context_update_tx(ctx, data->dfs, 1);
224 spi_context_update_rx(ctx, data->dfs, 1);
225 }
226
227 spi_context_cs_control(ctx, false);
228
229 spi_context_complete(ctx, dev, 0);
230
231 return 0;
232 }
233
234 #ifdef CONFIG_SPI_ASYNC
spi_bitbang_transceive_async(const struct device * dev,const struct spi_config * spi_cfg,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs,spi_callback_t cb,void * userdata)235 static int spi_bitbang_transceive_async(const struct device *dev,
236 const struct spi_config *spi_cfg,
237 const struct spi_buf_set *tx_bufs,
238 const struct spi_buf_set *rx_bufs,
239 spi_callback_t cb,
240 void *userdata)
241 {
242 return -ENOTSUP;
243 }
244 #endif
245
spi_bitbang_release(const struct device * dev,const struct spi_config * config)246 int spi_bitbang_release(const struct device *dev,
247 const struct spi_config *config)
248 {
249 struct spi_bitbang_data *data = dev->data;
250 struct spi_context *ctx = &data->ctx;
251
252 spi_context_unlock_unconditionally(ctx);
253 return 0;
254 }
255
256 static DEVICE_API(spi, spi_bitbang_api) = {
257 .transceive = spi_bitbang_transceive,
258 .release = spi_bitbang_release,
259 #ifdef CONFIG_SPI_ASYNC
260 .transceive_async = spi_bitbang_transceive_async,
261 #endif /* CONFIG_SPI_ASYNC */
262 #ifdef CONFIG_SPI_RTIO
263 .iodev_submit = spi_rtio_iodev_default_submit,
264 #endif
265 };
266
spi_bitbang_init(const struct device * dev)267 int spi_bitbang_init(const struct device *dev)
268 {
269 const struct spi_bitbang_config *config = dev->config;
270 struct spi_bitbang_data *data = dev->data;
271 int rc;
272
273 if (!gpio_is_ready_dt(&config->clk_gpio)) {
274 LOG_ERR("GPIO port for clk pin is not ready");
275 return -ENODEV;
276 }
277 rc = gpio_pin_configure_dt(&config->clk_gpio, GPIO_OUTPUT_INACTIVE);
278 if (rc < 0) {
279 LOG_ERR("Couldn't configure clk pin; (%d)", rc);
280 return rc;
281 }
282
283 if (config->mosi_gpio.port != NULL) {
284 if (!gpio_is_ready_dt(&config->mosi_gpio)) {
285 LOG_ERR("GPIO port for mosi pin is not ready");
286 return -ENODEV;
287 }
288 rc = gpio_pin_configure_dt(&config->mosi_gpio,
289 GPIO_OUTPUT_INACTIVE);
290 if (rc < 0) {
291 LOG_ERR("Couldn't configure mosi pin; (%d)", rc);
292 return rc;
293 }
294 }
295
296 if (config->miso_gpio.port != NULL) {
297 if (!gpio_is_ready_dt(&config->miso_gpio)) {
298 LOG_ERR("GPIO port for miso pin is not ready");
299 return -ENODEV;
300 }
301
302
303 rc = gpio_pin_configure_dt(&config->miso_gpio, GPIO_INPUT);
304 if (rc < 0) {
305 LOG_ERR("Couldn't configure miso pin; (%d)", rc);
306 return rc;
307 }
308 }
309
310 rc = spi_context_cs_configure_all(&data->ctx);
311 if (rc < 0) {
312 LOG_ERR("Failed to configure CS pins: %d", rc);
313 return rc;
314 }
315
316 return 0;
317 }
318
319 #define SPI_BITBANG_INIT(inst) \
320 static struct spi_bitbang_config spi_bitbang_config_##inst = { \
321 .clk_gpio = GPIO_DT_SPEC_INST_GET(inst, clk_gpios), \
322 .mosi_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mosi_gpios, {0}), \
323 .miso_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, miso_gpios, {0}), \
324 }; \
325 \
326 static struct spi_bitbang_data spi_bitbang_data_##inst = { \
327 SPI_CONTEXT_INIT_LOCK(spi_bitbang_data_##inst, ctx), \
328 SPI_CONTEXT_INIT_SYNC(spi_bitbang_data_##inst, ctx), \
329 SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(inst), ctx) \
330 }; \
331 \
332 SPI_DEVICE_DT_INST_DEFINE(inst, \
333 spi_bitbang_init, \
334 NULL, \
335 &spi_bitbang_data_##inst, \
336 &spi_bitbang_config_##inst, \
337 POST_KERNEL, \
338 CONFIG_SPI_INIT_PRIORITY, \
339 &spi_bitbang_api);
340
341 DT_INST_FOREACH_STATUS_OKAY(SPI_BITBANG_INIT)
342