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