blob: 63ea7699b872d8dc966bac84ca1fbd42b8977928 [file] [log] [blame]
Lionel Debieve094ab3d2019-09-24 17:38:12 +02001/*
2 * Copyright (c) 2019, STMicroelectronics - All Rights Reserved
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8
9#include <libfdt.h>
10
11#include <drivers/spi_mem.h>
12#include <lib/utils_def.h>
13
14#define SPI_MEM_DEFAULT_SPEED_HZ 100000U
15
16/*
17 * struct spi_slave - Representation of a SPI slave.
18 *
19 * @max_hz: Maximum speed for this slave in Hertz.
20 * @cs: ID of the chip select connected to the slave.
21 * @mode: SPI mode to use for this slave (see SPI mode flags).
22 * @ops: Ops defined by the bus.
23 */
24struct spi_slave {
25 unsigned int max_hz;
26 unsigned int cs;
27 unsigned int mode;
28 const struct spi_bus_ops *ops;
29};
30
31static struct spi_slave spi_slave;
32
33static bool spi_mem_check_buswidth_req(uint8_t buswidth, bool tx)
34{
35 switch (buswidth) {
36 case 1U:
37 return true;
38
39 case 2U:
40 if ((tx && (spi_slave.mode & (SPI_TX_DUAL | SPI_TX_QUAD)) !=
41 0U) ||
42 (!tx && (spi_slave.mode & (SPI_RX_DUAL | SPI_RX_QUAD)) !=
43 0U)) {
44 return true;
45 }
46 break;
47
48 case 4U:
49 if ((tx && (spi_slave.mode & SPI_TX_QUAD) != 0U) ||
50 (!tx && (spi_slave.mode & SPI_RX_QUAD) != 0U)) {
51 return true;
52 }
53 break;
54
55 default:
56 break;
57 }
58
59 return false;
60}
61
62static bool spi_mem_supports_op(const struct spi_mem_op *op)
63{
64 if (!spi_mem_check_buswidth_req(op->cmd.buswidth, true)) {
65 return false;
66 }
67
68 if ((op->addr.nbytes != 0U) &&
69 !spi_mem_check_buswidth_req(op->addr.buswidth, true)) {
70 return false;
71 }
72
73 if ((op->dummy.nbytes != 0U) &&
74 !spi_mem_check_buswidth_req(op->dummy.buswidth, true)) {
75 return false;
76 }
77
78 if ((op->data.nbytes != 0U) &&
79 !spi_mem_check_buswidth_req(op->data.buswidth,
80 op->data.dir == SPI_MEM_DATA_OUT)) {
81 return false;
82 }
83
84 return true;
85}
86
87static int spi_mem_set_speed_mode(void)
88{
89 const struct spi_bus_ops *ops = spi_slave.ops;
90 int ret;
91
92 ret = ops->set_speed(spi_slave.max_hz);
93 if (ret != 0) {
94 VERBOSE("Cannot set speed (err=%d)\n", ret);
95 return ret;
96 }
97
98 ret = ops->set_mode(spi_slave.mode);
99 if (ret != 0) {
100 VERBOSE("Cannot set mode (err=%d)\n", ret);
101 return ret;
102 }
103
104 return 0;
105}
106
107static int spi_mem_check_bus_ops(const struct spi_bus_ops *ops)
108{
109 bool error = false;
110
111 if (ops->claim_bus == NULL) {
112 VERBOSE("Ops claim bus is not defined\n");
113 error = true;
114 }
115
116 if (ops->release_bus == NULL) {
117 VERBOSE("Ops release bus is not defined\n");
118 error = true;
119 }
120
121 if (ops->exec_op == NULL) {
122 VERBOSE("Ops exec op is not defined\n");
123 error = true;
124 }
125
126 if (ops->set_speed == NULL) {
127 VERBOSE("Ops set speed is not defined\n");
128 error = true;
129 }
130
131 if (ops->set_mode == NULL) {
132 VERBOSE("Ops set mode is not defined\n");
133 error = true;
134 }
135
136 return error ? -EINVAL : 0;
137}
138
139/*
140 * spi_mem_exec_op() - Execute a memory operation.
141 * @op: The memory operation to execute.
142 *
143 * This function first checks that @op is supported and then tries to execute
144 * it.
145 *
146 * Return: 0 in case of success, a negative error code otherwise.
147 */
148int spi_mem_exec_op(const struct spi_mem_op *op)
149{
150 const struct spi_bus_ops *ops = spi_slave.ops;
151 int ret;
152
153 VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addqr:%llx len:%x\n",
154 __func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth,
155 op->dummy.buswidth, op->data.buswidth,
156 op->addr.val, op->data.nbytes);
157
158 if (!spi_mem_supports_op(op)) {
159 WARN("Error in spi_mem_support\n");
160 return -ENOTSUP;
161 }
162
163 ret = ops->claim_bus(spi_slave.cs);
164 if (ret != 0) {
165 WARN("Error claim_bus\n");
166 return ret;
167 }
168
169 ret = ops->exec_op(op);
170
171 ops->release_bus();
172
173 return ret;
174}
175
176/*
177 * spi_mem_init_slave() - SPI slave device initialization.
178 * @fdt: Pointer to the device tree blob.
179 * @bus_node: Offset of the bus node.
180 * @ops: The SPI bus ops defined.
181 *
182 * This function first checks that @ops are supported and then tries to find
183 * a SPI slave device.
184 *
185 * Return: 0 in case of success, a negative error code otherwise.
186 */
187int spi_mem_init_slave(void *fdt, int bus_node, const struct spi_bus_ops *ops)
188{
189 int ret;
190 int mode = 0;
191 int nchips = 0;
192 int bus_subnode = 0;
193 const fdt32_t *cuint = NULL;
194
195 ret = spi_mem_check_bus_ops(ops);
196 if (ret != 0) {
197 return ret;
198 }
199
200 fdt_for_each_subnode(bus_subnode, fdt, bus_node) {
201 nchips++;
202 }
203
204 if (nchips != 1) {
205 ERROR("Only one SPI device is currently supported\n");
206 return -EINVAL;
207 }
208
209 fdt_for_each_subnode(bus_subnode, fdt, bus_node) {
210 /* Get chip select */
211 cuint = fdt_getprop(fdt, bus_subnode, "reg", NULL);
212 if (cuint == NULL) {
213 ERROR("Chip select not well defined\n");
214 return -EINVAL;
215 }
216 spi_slave.cs = fdt32_to_cpu(*cuint);
217
218 /* Get max slave frequency */
219 spi_slave.max_hz = SPI_MEM_DEFAULT_SPEED_HZ;
220 cuint = fdt_getprop(fdt, bus_subnode,
221 "spi-max-frequency", NULL);
222 if (cuint != NULL) {
223 spi_slave.max_hz = fdt32_to_cpu(*cuint);
224 }
225
226 /* Get mode */
227 if ((fdt_getprop(fdt, bus_subnode, "spi-cpol", NULL)) != NULL) {
228 mode |= SPI_CPOL;
229 }
230 if ((fdt_getprop(fdt, bus_subnode, "spi-cpha", NULL)) != NULL) {
231 mode |= SPI_CPHA;
232 }
233 if ((fdt_getprop(fdt, bus_subnode, "spi-cs-high", NULL)) !=
234 NULL) {
235 mode |= SPI_CS_HIGH;
236 }
237 if ((fdt_getprop(fdt, bus_subnode, "spi-3wire", NULL)) !=
238 NULL) {
239 mode |= SPI_3WIRE;
240 }
241 if ((fdt_getprop(fdt, bus_subnode, "spi-half-duplex", NULL)) !=
242 NULL) {
243 mode |= SPI_PREAMBLE;
244 }
245
246 /* Get dual/quad mode */
247 cuint = fdt_getprop(fdt, bus_subnode, "spi-tx-bus-width", NULL);
248 if (cuint != NULL) {
249 switch (fdt32_to_cpu(*cuint)) {
250 case 1U:
251 break;
252 case 2U:
253 mode |= SPI_TX_DUAL;
254 break;
255 case 4U:
256 mode |= SPI_TX_QUAD;
257 break;
258 default:
259 WARN("spi-tx-bus-width %d not supported\n",
260 fdt32_to_cpu(*cuint));
261 return -EINVAL;
262 }
263 }
264
265 cuint = fdt_getprop(fdt, bus_subnode, "spi-rx-bus-width", NULL);
266 if (cuint != NULL) {
267 switch (fdt32_to_cpu(*cuint)) {
268 case 1U:
269 break;
270 case 2U:
271 mode |= SPI_RX_DUAL;
272 break;
273 case 4U:
274 mode |= SPI_RX_QUAD;
275 break;
276 default:
277 WARN("spi-rx-bus-width %d not supported\n",
278 fdt32_to_cpu(*cuint));
279 return -EINVAL;
280 }
281 }
282
283 spi_slave.mode = mode;
284 spi_slave.ops = ops;
285 }
286
287 return spi_mem_set_speed_mode();
288}