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