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 "spi_context.h"
16
17 struct spi_bitbang_data {
18 struct spi_context ctx;
19 int bits;
20 int wait_us;
21 int dfs;
22 };
23
24 struct spi_bitbang_config {
25 struct gpio_dt_spec clk_gpio;
26 struct gpio_dt_spec mosi_gpio;
27 struct gpio_dt_spec miso_gpio;
28 };
29
spi_bitbang_configure(const struct spi_bitbang_config * info,struct spi_bitbang_data * data,const struct spi_config * config)30 static int spi_bitbang_configure(const struct spi_bitbang_config *info,
31 struct spi_bitbang_data *data,
32 const struct spi_config *config)
33 {
34 if (config->operation & SPI_OP_MODE_SLAVE) {
35 LOG_ERR("Slave mode not supported");
36 return -ENOTSUP;
37 }
38
39 if (config->operation & (SPI_TRANSFER_LSB | SPI_LINES_DUAL
40 | SPI_LINES_QUAD)) {
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
128 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) {
129 clock_state = 1;
130 }
131 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) {
132 cpha = 1;
133 }
134 if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_LOOP) {
135 loop = true;
136 }
137
138 /* set the initial clock state before CS */
139 gpio_pin_set_dt(&info->clk_gpio, clock_state);
140
141 spi_context_cs_control(ctx, true);
142
143 const uint32_t wait_us = data->wait_us;
144
145 while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
146 uint16_t w = 0;
147
148 if (ctx->tx_len) {
149 switch (data->dfs) {
150 case 2:
151 w = *(uint16_t *)(ctx->tx_buf);
152 break;
153 case 1:
154 w = *(uint8_t *)(ctx->tx_buf);
155 break;
156 }
157 }
158
159 int shift = data->bits - 1;
160 uint16_t r = 0;
161 int b = 0;
162 bool do_read = false;
163
164 if (miso && spi_context_rx_buf_on(ctx)) {
165 do_read = true;
166 }
167
168 while (shift >= 0) {
169 const int d = (w >> shift) & 0x1;
170
171 b = 0;
172
173 /* setup data out first thing */
174 if (mosi) {
175 gpio_pin_set_dt(mosi, d);
176 }
177
178 k_busy_wait(wait_us);
179
180 /* first clock edge */
181 gpio_pin_set_dt(&info->clk_gpio, !clock_state);
182
183 if (!loop && do_read && !cpha) {
184 b = gpio_pin_get_dt(miso);
185 }
186
187 k_busy_wait(wait_us);
188
189 /* second clock edge */
190 gpio_pin_set_dt(&info->clk_gpio, clock_state);
191
192 if (!loop && do_read && cpha) {
193 b = gpio_pin_get_dt(miso);
194 }
195
196 if (loop) {
197 b = d;
198 }
199
200 r = (r << 1) | (b ? 0x1 : 0x0);
201
202 --shift;
203 }
204
205 if (spi_context_rx_buf_on(ctx)) {
206 switch (data->dfs) {
207 case 2:
208 *(uint16_t *)(ctx->rx_buf) = r;
209 break;
210 case 1:
211 *(uint8_t *)(ctx->rx_buf) = r;
212 break;
213 }
214 }
215
216 LOG_DBG(" w: %04x, r: %04x , do_read: %d", w, r, do_read);
217
218 spi_context_update_tx(ctx, data->dfs, 1);
219 spi_context_update_rx(ctx, data->dfs, 1);
220 }
221
222 spi_context_cs_control(ctx, false);
223
224 spi_context_complete(ctx, dev, 0);
225
226 return 0;
227 }
228
229 #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,struct k_poll_signal * async)230 static int spi_bitbang_transceive_async(const struct device *dev,
231 const struct spi_config *spi_cfg,
232 const struct spi_buf_set *tx_bufs,
233 const struct spi_buf_set *rx_bufs,
234 struct k_poll_signal *async)
235 {
236 return -ENOTSUP;
237 }
238 #endif
239
spi_bitbang_release(const struct device * dev,const struct spi_config * config)240 int spi_bitbang_release(const struct device *dev,
241 const struct spi_config *config)
242 {
243 struct spi_bitbang_data *data = dev->data;
244 struct spi_context *ctx = &data->ctx;
245
246 spi_context_unlock_unconditionally(ctx);
247 return 0;
248 }
249
250 static struct spi_driver_api spi_bitbang_api = {
251 .transceive = spi_bitbang_transceive,
252 .release = spi_bitbang_release,
253 #ifdef CONFIG_SPI_ASYNC
254 .transceive_async = spi_bitbang_transceive_async,
255 #endif /* CONFIG_SPI_ASYNC */
256 };
257
spi_bitbang_init(const struct device * dev)258 int spi_bitbang_init(const struct device *dev)
259 {
260 const struct spi_bitbang_config *config = dev->config;
261 struct spi_bitbang_data *data = dev->data;
262 int rc;
263
264 if (!gpio_is_ready_dt(&config->clk_gpio)) {
265 LOG_ERR("GPIO port for clk pin is not ready");
266 return -ENODEV;
267 }
268 rc = gpio_pin_configure_dt(&config->clk_gpio, GPIO_OUTPUT_INACTIVE);
269 if (rc < 0) {
270 LOG_ERR("Couldn't configure clk pin; (%d)", rc);
271 return rc;
272 }
273
274 if (config->mosi_gpio.port != NULL) {
275 if (!gpio_is_ready_dt(&config->mosi_gpio)) {
276 LOG_ERR("GPIO port for mosi pin is not ready");
277 return -ENODEV;
278 }
279 rc = gpio_pin_configure_dt(&config->mosi_gpio,
280 GPIO_OUTPUT_INACTIVE);
281 if (rc < 0) {
282 LOG_ERR("Couldn't configure mosi pin; (%d)", rc);
283 return rc;
284 }
285 }
286
287 if (config->miso_gpio.port != NULL) {
288 if (!gpio_is_ready_dt(&config->miso_gpio)) {
289 LOG_ERR("GPIO port for miso pin is not ready");
290 return -ENODEV;
291 }
292
293
294 rc = gpio_pin_configure_dt(&config->miso_gpio, GPIO_INPUT);
295 if (rc < 0) {
296 LOG_ERR("Couldn't configure miso pin; (%d)", rc);
297 return rc;
298 }
299 }
300
301 rc = spi_context_cs_configure_all(&data->ctx);
302 if (rc < 0) {
303 LOG_ERR("Failed to configure CS pins: %d", rc);
304 return rc;
305 }
306
307 return 0;
308 }
309
310 #define SPI_BITBANG_INIT(inst) \
311 static struct spi_bitbang_config spi_bitbang_config_##inst = { \
312 .clk_gpio = GPIO_DT_SPEC_INST_GET(inst, clk_gpios), \
313 .mosi_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, mosi_gpios, {0}), \
314 .miso_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, miso_gpios, {0}), \
315 }; \
316 \
317 static struct spi_bitbang_data spi_bitbang_data_##inst = { \
318 SPI_CONTEXT_INIT_LOCK(spi_bitbang_data_##inst, ctx), \
319 SPI_CONTEXT_INIT_SYNC(spi_bitbang_data_##inst, ctx), \
320 SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(inst), ctx) \
321 }; \
322 \
323 DEVICE_DT_INST_DEFINE(inst, \
324 spi_bitbang_init, \
325 NULL, \
326 &spi_bitbang_data_##inst, \
327 &spi_bitbang_config_##inst, \
328 POST_KERNEL, \
329 CONFIG_SPI_INIT_PRIORITY, \
330 &spi_bitbang_api);
331
332 DT_INST_FOREACH_STATUS_OKAY(SPI_BITBANG_INIT)
333