1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <interface/spi/spi.h>
18 #include <lib/spi/common/utils.h>
19 #include <lib/spi/srv/batch/dev.h>
20 #include <lib/spi/srv/common/common.h>
21 #include <lk/compiler.h>
22 #include <uapi/err.h>
23
24 #ifdef TRUSTY_USERSPACE
25 #define TLOG_TAG "spi-srv-batch"
26 #include <trusty_log.h>
27 #else
28 #include <stdio.h>
29 #define TLOGE(fmt, ...) \
30 fprintf(stderr, "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__)
31 #endif
32
handle_xfer_args(struct spi_dev_ctx * spi,struct mem_buf * shm)33 static int handle_xfer_args(struct spi_dev_ctx* spi, struct mem_buf* shm) {
34 int rc;
35 struct spi_xfer_args* xfer_args;
36 uint32_t xfer_args_len;
37 uint32_t xfer_args_flags;
38 void* payload;
39 void* tx;
40 void* rx;
41
42 xfer_args = mb_advance_pos(shm, sizeof(*xfer_args));
43 if (!xfer_args) {
44 TLOGE("failed to read SPI xfer request arguments from shared memory\n");
45 return ERR_NO_MEMORY;
46 }
47 xfer_args_len = READ_ONCE(xfer_args->len);
48 xfer_args_flags = READ_ONCE(xfer_args->flags);
49
50 payload = mb_advance_pos(shm, xfer_args_len);
51 if (!payload) {
52 TLOGE("failed to get payload from shared memory\n");
53 return ERR_NO_MEMORY;
54 }
55
56 tx = (xfer_args_flags & SPI_XFER_FLAGS_TX) ? payload : NULL;
57 rx = (xfer_args_flags & SPI_XFER_FLAGS_RX) ? payload : NULL;
58
59 rc = spi_req_xfer(spi, tx, rx, xfer_args_len);
60 if (rc != NO_ERROR) {
61 TLOGE("spi xfer failed (%d)\n", rc);
62 }
63
64 /* don't modify @xfer_args as a response */
65 return rc;
66 }
67
handle_clk_args(struct spi_dev_ctx * spi,struct mem_buf * shm)68 static int handle_clk_args(struct spi_dev_ctx* spi, struct mem_buf* shm) {
69 int rc;
70 struct spi_clk_args* clk_args;
71
72 clk_args = mb_advance_pos(shm, sizeof(*clk_args));
73 if (!clk_args) {
74 TLOGE("failed to read SPI clk request arguments from shared memory\n");
75 return ERR_NO_MEMORY;
76 }
77
78 rc = spi_req_set_clk(spi, &clk_args->clk_hz);
79 if (rc != NO_ERROR) {
80 TLOGE("failed (%d) to set SPI clock speed\n", rc);
81 }
82
83 /* @clk_args response is handled by driver implementation */
84 return rc;
85 }
86
handle_delay_args(struct spi_dev_ctx * spi,struct mem_buf * shm)87 static int handle_delay_args(struct spi_dev_ctx* spi, struct mem_buf* shm) {
88 int rc = 0;
89 struct spi_delay_args* delay_args;
90
91 delay_args = mb_advance_pos(shm, sizeof(*delay_args));
92 if (!delay_args) {
93 TLOGE("failed to read delay request arguments from shared memory\n");
94 return ERR_NO_MEMORY;
95 }
96
97 rc = spi_req_delay(spi, delay_args->delay_ns);
98 if (rc != NO_ERROR) {
99 TLOGE("failed (%d) to request delay\n", rc);
100 }
101
102 delay_args->delay_ns = 0;
103 return rc;
104 }
105
unpack_shm(struct spi_dev_ctx * spi,struct mem_buf * shm,size_t len,struct spi_batch_state * state)106 static int unpack_shm(struct spi_dev_ctx* spi,
107 struct mem_buf* shm,
108 size_t len,
109 struct spi_batch_state* state) {
110 int rc;
111 struct spi_shm_hdr* shm_hdr;
112 uint32_t shm_hdr_cmd;
113
114 /*
115 * Resize @shm, so that we don't process more than batch length. And rewind
116 * @shm position back to the beginning.
117 */
118 mb_resize(shm, len);
119
120 while (mb_curr_pos(shm) < len) {
121 shm_hdr = mb_advance_pos(shm, sizeof(*shm_hdr));
122 if (!shm_hdr) {
123 TLOGE("failed to read spi_shm_hdr in shared memory\n");
124 return ERR_NO_MEMORY;
125 }
126 shm_hdr_cmd = READ_ONCE(shm_hdr->cmd);
127
128 switch (shm_hdr_cmd) {
129 case SPI_CMD_SHM_OP_XFER:
130 if (state->cs) {
131 rc = handle_xfer_args(spi, shm);
132 } else {
133 rc = ERR_NOT_READY;
134 }
135 break;
136
137 case SPI_CMD_SHM_OP_CS_ASSERT:
138 if (state->cs) {
139 rc = ERR_BUSY;
140 } else {
141 rc = spi_req_cs_assert(spi);
142 state->cs = true;
143 }
144 break;
145
146 case SPI_CMD_SHM_OP_CS_DEASSERT:
147 if (state->cs) {
148 rc = spi_req_cs_deassert(spi);
149 state->cs = false;
150 } else {
151 rc = ERR_NOT_READY;
152 }
153 break;
154
155 case SPI_CMD_SHM_OP_SET_CLK:
156 rc = handle_clk_args(spi, shm);
157 break;
158
159 case SPI_CMD_SHM_OP_DELAY:
160 rc = handle_delay_args(spi, shm);
161 break;
162
163 default:
164 TLOGE("cmd 0x%x: unknown command\n", shm_hdr_cmd);
165 rc = ERR_CMD_UNKNOWN;
166 }
167
168 WRITE_ONCE(shm_hdr->cmd, shm_hdr_cmd | SPI_CMD_RESP_BIT);
169 WRITE_ONCE(shm_hdr->status, translate_lk_err(rc));
170
171 if (rc != NO_ERROR) {
172 TLOGE("failed (%d) to unpack SPI request at index: %zu\n", rc,
173 state->num_cmds);
174 return rc;
175 }
176 state->num_cmds++;
177 }
178
179 return NO_ERROR;
180 }
181
spi_srv_handle_batch(struct spi_dev_ctx * spi,struct mem_buf * shm,struct spi_batch_req * batch_req,struct spi_batch_state * state)182 int spi_srv_handle_batch(struct spi_dev_ctx* spi,
183 struct mem_buf* shm,
184 struct spi_batch_req* batch_req,
185 struct spi_batch_state* state) {
186 int rc = NO_ERROR;
187
188 if (batch_req->len > shm->capacity) {
189 TLOGE("requests batch size(%d) is larger than shared memory(%zu)\n",
190 batch_req->len, shm->capacity);
191 return ERR_TOO_BIG;
192 }
193
194 /* SPI devices with shared bus must be deasserted before command sequence */
195 assert(!state->cs || !spi_is_bus_shared(spi));
196
197 rc = spi_seq_begin(spi, batch_req->num_cmds);
198 if (rc != NO_ERROR) {
199 TLOGE("failed (%d) to begin SPI requests\n", rc);
200 return rc;
201 }
202
203 rc = unpack_shm(spi, shm, batch_req->len, state);
204 if (rc != NO_ERROR) {
205 TLOGE("failed (%d) to unpack SPI requests, aborting sequence\n", rc);
206 goto err;
207 }
208
209 if (state->num_cmds != batch_req->num_cmds) {
210 TLOGE("number of commands in shared memory(%zu) and in request(%d) "
211 "are different\n",
212 state->num_cmds, batch_req->num_cmds);
213 rc = ERR_INVALID_ARGS;
214 goto err;
215 }
216
217 if (mb_curr_pos(shm) != batch_req->len) {
218 TLOGE("response size (%zu) and request size (%d) are different\n",
219 mb_curr_pos(shm), batch_req->len);
220 rc = ERR_BAD_LEN;
221 goto err;
222 }
223
224 /* SPI devices with shared bus must be deasserted after command sequence */
225 if (state->cs && spi_is_bus_shared(spi)) {
226 rc = ERR_BAD_STATE;
227 goto err;
228 }
229
230 rc = spi_seq_commit(spi);
231 if (rc != NO_ERROR) {
232 TLOGE("failed (%d) to commit SPI requests\n", rc);
233 goto err;
234 }
235
236 return NO_ERROR;
237
238 err:
239 spi_seq_abort(spi);
240 return rc;
241 }
242