1 /*
2  * Copyright (c) 2019 Western Digital Corporation or its affiliates
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT opencores_spi_simple
8 
9 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(spi_oc_simple);
12 
13 #include <zephyr/sys/sys_io.h>
14 #include <zephyr/drivers/spi.h>
15 #include <zephyr/drivers/spi/rtio.h>
16 
17 #include "spi_context.h"
18 #include "spi_oc_simple.h"
19 
20 /* Bit 5:4 == ESPR, Bit 1:0 == SPR */
21 uint8_t DIVIDERS[] = { 0x00,       /*   2  */
22 		    0x01,       /*   4  */
23 		    0x10,       /*   8  */
24 		    0x02,       /*  16  */
25 		    0x03,       /*  32  */
26 		    0x11,       /*  64  */
27 		    0x12,       /* 128  */
28 		    0x13,       /* 256  */
29 		    0x20,       /* 512  */
30 		    0x21,       /* 1024 */
31 		    0x22,       /* 2048 */
32 		    0x23 };     /* 4096 */
33 
spi_oc_simple_configure(const struct spi_oc_simple_cfg * info,struct spi_oc_simple_data * spi,const struct spi_config * config)34 static int spi_oc_simple_configure(const struct spi_oc_simple_cfg *info,
35 				struct spi_oc_simple_data *spi,
36 				const struct spi_config *config)
37 {
38 	uint8_t spcr = 0U;
39 	int i;
40 
41 	if (spi_context_configured(&spi->ctx, config)) {
42 		/* Nothing to do */
43 		return 0;
44 	}
45 
46 	if (config->operation & SPI_HALF_DUPLEX) {
47 		LOG_ERR("Half-duplex not supported");
48 		return -ENOTSUP;
49 	}
50 
51 	/* Simple SPI only supports master mode */
52 	if (spi_context_is_slave(&spi->ctx)) {
53 		LOG_ERR("Slave mode not supported");
54 		return -ENOTSUP;
55 	}
56 
57 	if ((config->operation & (SPI_MODE_LOOP | SPI_TRANSFER_LSB)) ||
58 	    (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
59 	     (config->operation &
60 	      (SPI_LINES_DUAL | SPI_LINES_QUAD | SPI_LINES_OCTAL)))) {
61 		LOG_ERR("Unsupported configuration");
62 		return -EINVAL;
63 	}
64 
65 	/* SPI mode */
66 	if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
67 		spcr |= SPI_OC_SIMPLE_SPCR_CPOL;
68 	}
69 
70 	if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
71 		spcr |= SPI_OC_SIMPLE_SPCR_CPHA;
72 	}
73 
74 	/* Set clock divider */
75 	for (i = 0; i < 12; i++) {
76 		if ((config->frequency << (i + 1)) >
77 		    CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) {
78 			break;
79 		}
80 	}
81 
82 	sys_write8((DIVIDERS[i] >> 4) & 0x3, SPI_OC_SIMPLE_SPER(info));
83 	spcr |= (DIVIDERS[i] & 0x3);
84 
85 	/* Configure and Enable SPI controller */
86 	sys_write8(spcr | SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info));
87 
88 	spi->ctx.config = config;
89 
90 	return 0;
91 }
92 
spi_oc_simple_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)93 int spi_oc_simple_transceive(const struct device *dev,
94 			     const struct spi_config *config,
95 			     const struct spi_buf_set *tx_bufs,
96 			     const struct spi_buf_set *rx_bufs)
97 {
98 	const struct spi_oc_simple_cfg *info = dev->config;
99 	struct spi_oc_simple_data *spi = SPI_OC_SIMPLE_DATA(dev);
100 	struct spi_context *ctx = &spi->ctx;
101 
102 	uint8_t rx_byte;
103 	size_t i;
104 	size_t cur_xfer_len;
105 	int rc;
106 
107 	/* Lock the SPI Context */
108 	spi_context_lock(ctx, false, NULL, NULL, config);
109 
110 	spi_oc_simple_configure(info, spi, config);
111 
112 	/* Set chip select */
113 	if (spi_cs_is_gpio(config)) {
114 		spi_context_cs_control(&spi->ctx, true);
115 	} else {
116 		sys_write8(1 << config->slave, SPI_OC_SIMPLE_SPSS(info));
117 	}
118 
119 	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
120 
121 	while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
122 		cur_xfer_len = spi_context_longest_current_buf(ctx);
123 
124 		for (i = 0; i < cur_xfer_len; i++) {
125 
126 			/* Write byte */
127 			if (spi_context_tx_buf_on(ctx)) {
128 				sys_write8(*ctx->tx_buf,
129 					   SPI_OC_SIMPLE_SPDR(info));
130 				spi_context_update_tx(ctx, 1, 1);
131 			} else {
132 				sys_write8(0, SPI_OC_SIMPLE_SPDR(info));
133 			}
134 
135 			/* Wait for rx FIFO empty flag to clear */
136 			while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) {
137 			}
138 
139 			/* Get received byte */
140 			rx_byte = sys_read8(SPI_OC_SIMPLE_SPDR(info));
141 
142 			/* Store received byte if rx buffer is on */
143 			if (spi_context_rx_on(ctx)) {
144 				*ctx->rx_buf = rx_byte;
145 				spi_context_update_rx(ctx, 1, 1);
146 			}
147 		}
148 	}
149 
150 	/* Clear chip-select */
151 	if (spi_cs_is_gpio(config)) {
152 		spi_context_cs_control(&spi->ctx, false);
153 	} else {
154 		sys_write8(0 << config->slave, SPI_OC_SIMPLE_SPSS(info));
155 	}
156 
157 	spi_context_complete(ctx, dev, 0);
158 	rc = spi_context_wait_for_completion(ctx);
159 
160 	spi_context_release(ctx, rc);
161 
162 	return rc;
163 }
164 
165 #ifdef CONFIG_SPI_ASYNC
spi_oc_simple_transceive_async(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs,struct k_poll_signal * async)166 static int spi_oc_simple_transceive_async(const struct device *dev,
167 					  const struct spi_config *config,
168 					  const struct spi_buf_set *tx_bufs,
169 					  const struct spi_buf_set *rx_bufs,
170 					  struct k_poll_signal *async)
171 {
172 	return -ENOTSUP;
173 }
174 #endif /* CONFIG_SPI_ASYNC */
175 
spi_oc_simple_release(const struct device * dev,const struct spi_config * config)176 int spi_oc_simple_release(const struct device *dev,
177 			  const struct spi_config *config)
178 {
179 	spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx);
180 	return 0;
181 }
182 
183 static DEVICE_API(spi, spi_oc_simple_api) = {
184 	.transceive = spi_oc_simple_transceive,
185 	.release = spi_oc_simple_release,
186 #ifdef CONFIG_SPI_ASYNC
187 	.transceive_async = spi_oc_simple_transceive_async,
188 #endif /* CONFIG_SPI_ASYNC */
189 #ifdef CONFIG_SPI_RTIO
190 	.iodev_submit = spi_rtio_iodev_default_submit,
191 #endif
192 
193 };
194 
spi_oc_simple_init(const struct device * dev)195 int spi_oc_simple_init(const struct device *dev)
196 {
197 	int err;
198 	const struct spi_oc_simple_cfg *info = dev->config;
199 	struct spi_oc_simple_data *data = dev->data;
200 
201 	/* Clear chip selects */
202 	sys_write8(0, SPI_OC_SIMPLE_SPSS(info));
203 
204 	err = spi_context_cs_configure_all(&data->ctx);
205 	if (err < 0) {
206 		return err;
207 	}
208 
209 	/* Make sure the context is unlocked */
210 	spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx);
211 
212 	/* Initial clock stucks high, so add this workaround */
213 	sys_write8(SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info));
214 	sys_write8(0, SPI_OC_SIMPLE_SPDR(info));
215 	while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) {
216 	}
217 
218 	sys_read8(SPI_OC_SIMPLE_SPDR(info));
219 
220 	return 0;
221 }
222 
223 #define SPI_OC_INIT(inst)						\
224 	static struct spi_oc_simple_cfg spi_oc_simple_cfg_##inst = {	\
225 		.base = DT_INST_REG_ADDR_BY_NAME(inst, control),	\
226 	};								\
227 									\
228 	static struct spi_oc_simple_data spi_oc_simple_data_##inst = {	\
229 		SPI_CONTEXT_INIT_LOCK(spi_oc_simple_data_##inst, ctx),	\
230 		SPI_CONTEXT_INIT_SYNC(spi_oc_simple_data_##inst, ctx),	\
231 		SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(inst), ctx) \
232 	};								\
233 									\
234 	SPI_DEVICE_DT_INST_DEFINE(inst,					\
235 			    spi_oc_simple_init,				\
236 			    NULL,					\
237 			    &spi_oc_simple_data_##inst,			\
238 			    &spi_oc_simple_cfg_##inst,			\
239 			    POST_KERNEL,				\
240 			    CONFIG_SPI_INIT_PRIORITY,			\
241 			    &spi_oc_simple_api);
242 
243 DT_INST_FOREACH_STATUS_OKAY(SPI_OC_INIT)
244