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