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