• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
24 struct spi_slave {
25 	unsigned int max_hz;
26 	unsigned int cs;
27 	unsigned int mode;
28 	const struct spi_bus_ops *ops;
29 };
30 
31 static struct spi_slave spi_slave;
32 
spi_mem_check_buswidth_req(uint8_t buswidth,bool tx)33 static 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 
spi_mem_supports_op(const struct spi_mem_op * op)62 static 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 
spi_mem_set_speed_mode(void)87 static 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 
spi_mem_check_bus_ops(const struct spi_bus_ops * ops)107 static 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  */
spi_mem_exec_op(const struct spi_mem_op * op)148 int 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  */
spi_mem_init_slave(void * fdt,int bus_node,const struct spi_bus_ops * ops)187 int 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 }
289