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