blob: 10de819f93e344eb88d62b9e64b33b753e711eac [file] [log] [blame]
Olof Kindgrend09614a2019-09-11 22:16:28 +02001/*
2 * Copyright (c) 2019 Western Digital Corporation or its affiliates
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
Kumar Gala73c1f8d2020-03-25 11:38:11 -05007#define DT_DRV_COMPAT opencores_spi_simple
8
Olof Kindgrend09614a2019-09-11 22:16:28 +02009#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
10#include <logging/log.h>
11LOG_MODULE_REGISTER(spi_oc_simple);
12
Kumar Galacd0f8d52019-12-09 11:31:24 -060013#include <sys/sys_io.h>
Peter Bigot5ceb6122020-01-25 05:35:02 -060014#include <drivers/spi.h>
Olof Kindgrend09614a2019-09-11 22:16:28 +020015
16#include "spi_context.h"
17#include "spi_oc_simple.h"
18
19/* Bit 5:4 == ESPR, Bit 1:0 == SPR */
Kumar Galaa1b77fd2020-05-27 11:26:57 -050020uint8_t DIVIDERS[] = { 0x00, /* 2 */
Olof Kindgrend09614a2019-09-11 22:16:28 +020021 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
33static 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{
Kumar Galaa1b77fd2020-05-27 11:26:57 -050037 uint8_t spcr = 0U;
Olof Kindgrend09614a2019-09-11 22:16:28 +020038 int i;
39
40 if (spi_context_configured(&spi->ctx, config)) {
41 /* Nothing to do */
42 return 0;
43 }
44
45 /* Simple SPI only supports master mode */
46 if (spi_context_is_slave(&spi->ctx)) {
47 LOG_ERR("Slave mode not supported");
48 return -ENOTSUP;
49 }
50
51 if (config->operation & (SPI_MODE_LOOP | SPI_TRANSFER_LSB |
52 SPI_LINES_DUAL | SPI_LINES_QUAD)) {
53 LOG_ERR("Unsupported configuration");
54 return -EINVAL;
55 }
56
57 /* SPI mode */
58 if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
59 spcr |= SPI_OC_SIMPLE_SPCR_CPOL;
60 }
61
62 if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
63 spcr |= SPI_OC_SIMPLE_SPCR_CPHA;
64 }
65
66 /* Set clock divider */
67 for (i = 0; i < 12; i++)
68 if ((config->frequency << (i + 1)) >
69 CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) {
70 break;
71 }
72
73 sys_write8((DIVIDERS[i] >> 4) & 0x3, SPI_OC_SIMPLE_SPER(info));
74 spcr |= (DIVIDERS[i] & 0x3);
75
76 /* Configure and Enable SPI controller */
77 sys_write8(spcr | SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info));
78
79 spi->ctx.config = config;
80
81 if (config->cs) {
82 spi_context_cs_configure(&spi->ctx);
83 }
84
85 return 0;
86}
87
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +020088int spi_oc_simple_transceive(const struct device *dev,
89 const struct spi_config *config,
90 const struct spi_buf_set *tx_bufs,
91 const struct spi_buf_set *rx_bufs)
Olof Kindgrend09614a2019-09-11 22:16:28 +020092{
Tomasz Bursztykaaf6140c2020-05-28 20:44:16 +020093 const struct spi_oc_simple_cfg *info = dev->config;
Olof Kindgrend09614a2019-09-11 22:16:28 +020094 struct spi_oc_simple_data *spi = SPI_OC_SIMPLE_DATA(dev);
95 struct spi_context *ctx = &spi->ctx;
96
Kumar Galaa1b77fd2020-05-27 11:26:57 -050097 uint8_t rx_byte;
Olof Kindgrend09614a2019-09-11 22:16:28 +020098 size_t i;
99 size_t cur_xfer_len;
100 int rc;
101
102 /* Lock the SPI Context */
Stefan Bigler596cad82020-10-19 08:52:29 +0200103 spi_context_lock(ctx, false, NULL, config);
Olof Kindgrend09614a2019-09-11 22:16:28 +0200104
105 spi_oc_simple_configure(info, spi, config);
106
107 /* Set chip select */
108 if (config->cs) {
109 spi_context_cs_control(&spi->ctx, true);
110 } else {
111 sys_write8(1 << config->slave, SPI_OC_SIMPLE_SPSS(info));
112 }
113
114 spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
115
116 while (spi_context_tx_buf_on(ctx) || spi_context_rx_buf_on(ctx)) {
117 cur_xfer_len = spi_context_longest_current_buf(ctx);
118
119 for (i = 0; i < cur_xfer_len; i++) {
120
121 /* Write byte */
122 if (spi_context_tx_buf_on(ctx)) {
123 sys_write8(*ctx->tx_buf,
124 SPI_OC_SIMPLE_SPDR(info));
125 spi_context_update_tx(ctx, 1, 1);
126 } else {
127 sys_write8(0, SPI_OC_SIMPLE_SPDR(info));
128 }
129
130 /* Wait for rx FIFO empty flag to clear */
131 while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) {
132 }
133
134 /* Get received byte */
135 rx_byte = sys_read8(SPI_OC_SIMPLE_SPDR(info));
136
137 /* Store received byte if rx buffer is on */
138 if (spi_context_rx_on(ctx)) {
139 *ctx->rx_buf = rx_byte;
140 spi_context_update_rx(ctx, 1, 1);
141 }
142 }
143 }
144
145 /* Clear chip-select */
146 if (config->cs) {
147 spi_context_cs_control(&spi->ctx, false);
148 } else {
149 sys_write8(0 << config->slave, SPI_OC_SIMPLE_SPSS(info));
150 }
151
152 spi_context_complete(ctx, 0);
153 rc = spi_context_wait_for_completion(ctx);
154
155 spi_context_release(ctx, rc);
156
157 return rc;
158}
159
160#ifdef CONFIG_SPI_ASYNC
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200161static int spi_oc_simple_transceive_async(const struct device *dev,
162 const struct spi_config *config,
163 const struct spi_buf_set *tx_bufs,
164 const struct spi_buf_set *rx_bufs,
165 struct k_poll_signal *async)
Olof Kindgrend09614a2019-09-11 22:16:28 +0200166{
167 return -ENOTSUP;
168}
169#endif /* CONFIG_SPI_ASYNC */
170
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200171int spi_oc_simple_release(const struct device *dev,
172 const struct spi_config *config)
Olof Kindgrend09614a2019-09-11 22:16:28 +0200173{
174 spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx);
175 return 0;
176}
177
178static struct spi_driver_api spi_oc_simple_api = {
179 .transceive = spi_oc_simple_transceive,
180 .release = spi_oc_simple_release,
181#ifdef CONFIG_SPI_ASYNC
182 .transceive_async = spi_oc_simple_transceive_async,
183#endif /* CONFIG_SPI_ASYNC */
184};
185
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200186int spi_oc_simple_init(const struct device *dev)
Olof Kindgrend09614a2019-09-11 22:16:28 +0200187{
Tomasz Bursztykaaf6140c2020-05-28 20:44:16 +0200188 const struct spi_oc_simple_cfg *info = dev->config;
Olof Kindgrend09614a2019-09-11 22:16:28 +0200189
190 /* Clear chip selects */
191 sys_write8(0, SPI_OC_SIMPLE_SPSS(info));
192
193 /* Make sure the context is unlocked */
194 spi_context_unlock_unconditionally(&SPI_OC_SIMPLE_DATA(dev)->ctx);
195
196 /* Initial clock stucks high, so add this workaround */
197 sys_write8(SPI_OC_SIMPLE_SPCR_SPE, SPI_OC_SIMPLE_SPCR(info));
198 sys_write8(0, SPI_OC_SIMPLE_SPDR(info));
199 while (sys_read8(SPI_OC_SIMPLE_SPSR(info)) & 0x1) {
200 }
201
202 sys_read8(SPI_OC_SIMPLE_SPDR(info));
203
204 return 0;
205}
206
Martí Bolívar87e17432020-05-05 16:06:32 -0700207#define SPI_OC_INIT(inst) \
208 static struct spi_oc_simple_cfg spi_oc_simple_cfg_##inst = { \
209 .base = DT_INST_REG_ADDR_BY_NAME(inst, control), \
210 }; \
211 \
212 static struct spi_oc_simple_data spi_oc_simple_data_##inst = { \
213 SPI_CONTEXT_INIT_LOCK(spi_oc_simple_data_##inst, ctx), \
214 SPI_CONTEXT_INIT_SYNC(spi_oc_simple_data_##inst, ctx), \
215 }; \
216 \
Kumar Gala03ad31b2020-12-09 12:32:41 -0600217 DEVICE_DT_INST_DEFINE(inst, \
Martí Bolívar87e17432020-05-05 16:06:32 -0700218 spi_oc_simple_init, \
Kumar Gala03ad31b2020-12-09 12:32:41 -0600219 device_pm_control_nop, \
Martí Bolívar87e17432020-05-05 16:06:32 -0700220 &spi_oc_simple_data_##inst, \
221 &spi_oc_simple_cfg_##inst, \
222 POST_KERNEL, \
223 CONFIG_SPI_INIT_PRIORITY, \
Kumar Gala0a7d4e22020-05-07 14:09:05 -0500224 &spi_oc_simple_api);
Olof Kindgrend09614a2019-09-11 22:16:28 +0200225
Martí Bolívar7e0eed92020-05-06 11:23:07 -0700226DT_INST_FOREACH_STATUS_OKAY(SPI_OC_INIT)