]>
Commit | Line | Data |
---|---|---|
caf989c3 BA |
1 | /* |
2 | * Copyright (c) 2016, Linaro Ltd | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 and | |
6 | * only version 2 as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <linux/io.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/of.h> | |
17 | #include <linux/of_address.h> | |
18 | #include <linux/interrupt.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/mfd/syscon.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/rpmsg.h> | |
23 | #include <linux/idr.h> | |
24 | #include <linux/circ_buf.h> | |
25 | #include <linux/soc/qcom/smem.h> | |
26 | #include <linux/sizes.h> | |
27 | #include <linux/delay.h> | |
28 | #include <linux/regmap.h> | |
29 | #include <linux/workqueue.h> | |
30 | #include <linux/list.h> | |
31 | ||
caf989c3 BA |
32 | #include <linux/rpmsg/qcom_glink.h> |
33 | ||
34 | #include "qcom_glink_native.h" | |
35 | ||
36 | #define FIFO_FULL_RESERVE 8 | |
37 | #define FIFO_ALIGNMENT 8 | |
38 | #define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */ | |
39 | ||
40 | #define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR 478 | |
41 | #define SMEM_GLINK_NATIVE_XPRT_FIFO_0 479 | |
42 | #define SMEM_GLINK_NATIVE_XPRT_FIFO_1 480 | |
43 | ||
44 | struct glink_smem_pipe { | |
45 | struct qcom_glink_pipe native; | |
46 | ||
47 | __le32 *tail; | |
48 | __le32 *head; | |
49 | ||
50 | void *fifo; | |
51 | ||
52 | int remote_pid; | |
53 | }; | |
54 | ||
55 | #define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native) | |
56 | ||
57 | static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np) | |
58 | { | |
59 | struct glink_smem_pipe *pipe = to_smem_pipe(np); | |
60 | size_t len; | |
61 | void *fifo; | |
62 | u32 head; | |
63 | u32 tail; | |
64 | ||
65 | if (!pipe->fifo) { | |
66 | fifo = qcom_smem_get(pipe->remote_pid, | |
67 | SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len); | |
68 | if (IS_ERR(fifo)) { | |
69 | pr_err("failed to acquire RX fifo handle: %ld\n", | |
70 | PTR_ERR(fifo)); | |
71 | return 0; | |
72 | } | |
73 | ||
74 | pipe->fifo = fifo; | |
75 | pipe->native.length = len; | |
76 | } | |
77 | ||
78 | head = le32_to_cpu(*pipe->head); | |
79 | tail = le32_to_cpu(*pipe->tail); | |
80 | ||
81 | if (head < tail) | |
82 | return pipe->native.length - tail + head; | |
83 | else | |
84 | return head - tail; | |
85 | } | |
86 | ||
87 | static void glink_smem_rx_peak(struct qcom_glink_pipe *np, | |
b88eee97 | 88 | void *data, unsigned int offset, size_t count) |
caf989c3 BA |
89 | { |
90 | struct glink_smem_pipe *pipe = to_smem_pipe(np); | |
91 | size_t len; | |
92 | u32 tail; | |
93 | ||
94 | tail = le32_to_cpu(*pipe->tail); | |
b88eee97 BA |
95 | tail += offset; |
96 | if (tail >= pipe->native.length) | |
97 | tail -= pipe->native.length; | |
caf989c3 BA |
98 | |
99 | len = min_t(size_t, count, pipe->native.length - tail); | |
100 | if (len) { | |
101 | __ioread32_copy(data, pipe->fifo + tail, | |
102 | len / sizeof(u32)); | |
103 | } | |
104 | ||
105 | if (len != count) { | |
106 | __ioread32_copy(data + len, pipe->fifo, | |
107 | (count - len) / sizeof(u32)); | |
108 | } | |
109 | } | |
110 | ||
111 | static void glink_smem_rx_advance(struct qcom_glink_pipe *np, | |
112 | size_t count) | |
113 | { | |
114 | struct glink_smem_pipe *pipe = to_smem_pipe(np); | |
115 | u32 tail; | |
116 | ||
117 | tail = le32_to_cpu(*pipe->tail); | |
118 | ||
119 | tail += count; | |
120 | if (tail > pipe->native.length) | |
121 | tail -= pipe->native.length; | |
122 | ||
123 | *pipe->tail = cpu_to_le32(tail); | |
124 | } | |
125 | ||
126 | static size_t glink_smem_tx_avail(struct qcom_glink_pipe *np) | |
127 | { | |
128 | struct glink_smem_pipe *pipe = to_smem_pipe(np); | |
129 | u32 head; | |
130 | u32 tail; | |
131 | u32 avail; | |
132 | ||
133 | head = le32_to_cpu(*pipe->head); | |
134 | tail = le32_to_cpu(*pipe->tail); | |
135 | ||
136 | if (tail <= head) | |
137 | avail = pipe->native.length - head + tail; | |
138 | else | |
139 | avail = tail - head; | |
140 | ||
141 | if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE)) | |
142 | avail = 0; | |
143 | else | |
144 | avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE; | |
145 | ||
146 | return avail; | |
147 | } | |
148 | ||
149 | static unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe, | |
150 | unsigned int head, | |
151 | const void *data, size_t count) | |
152 | { | |
153 | size_t len; | |
154 | ||
155 | len = min_t(size_t, count, pipe->native.length - head); | |
156 | if (len) | |
157 | memcpy(pipe->fifo + head, data, len); | |
158 | ||
159 | if (len != count) | |
160 | memcpy(pipe->fifo, data + len, count - len); | |
161 | ||
162 | head += count; | |
163 | if (head >= pipe->native.length) | |
164 | head -= pipe->native.length; | |
165 | ||
166 | return head; | |
167 | } | |
168 | ||
169 | static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe, | |
170 | const void *hdr, size_t hlen, | |
171 | const void *data, size_t dlen) | |
172 | { | |
173 | struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe); | |
174 | unsigned int head; | |
175 | ||
176 | head = le32_to_cpu(*pipe->head); | |
177 | ||
178 | head = glink_smem_tx_write_one(pipe, head, hdr, hlen); | |
179 | head = glink_smem_tx_write_one(pipe, head, data, dlen); | |
180 | ||
181 | /* Ensure head is always aligned to 8 bytes */ | |
182 | head = ALIGN(head, 8); | |
183 | if (head >= pipe->native.length) | |
184 | head -= pipe->native.length; | |
185 | ||
186 | *pipe->head = cpu_to_le32(head); | |
187 | } | |
188 | ||
189 | static void qcom_glink_smem_release(struct device *dev) | |
190 | { | |
191 | kfree(dev); | |
192 | } | |
193 | ||
194 | struct qcom_glink *qcom_glink_smem_register(struct device *parent, | |
195 | struct device_node *node) | |
196 | { | |
197 | struct glink_smem_pipe *rx_pipe; | |
198 | struct glink_smem_pipe *tx_pipe; | |
199 | struct qcom_glink *glink; | |
200 | struct device *dev; | |
201 | u32 remote_pid; | |
202 | __le32 *descs; | |
203 | size_t size; | |
204 | int ret; | |
205 | ||
206 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
207 | if (!dev) | |
208 | return ERR_PTR(-ENOMEM); | |
209 | ||
210 | dev->parent = parent; | |
211 | dev->of_node = node; | |
212 | dev->release = qcom_glink_smem_release; | |
213 | dev_set_name(dev, "%s:%s", node->parent->name, node->name); | |
214 | ret = device_register(dev); | |
215 | if (ret) { | |
216 | pr_err("failed to register glink edge\n"); | |
217 | return ERR_PTR(ret); | |
218 | } | |
219 | ||
220 | ret = of_property_read_u32(dev->of_node, "qcom,remote-pid", | |
221 | &remote_pid); | |
222 | if (ret) { | |
223 | dev_err(dev, "failed to parse qcom,remote-pid\n"); | |
224 | goto err_put_dev; | |
225 | } | |
226 | ||
227 | rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL); | |
228 | tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL); | |
229 | if (!rx_pipe || !tx_pipe) { | |
230 | ret = -ENOMEM; | |
231 | goto err_put_dev; | |
232 | } | |
233 | ||
234 | ret = qcom_smem_alloc(remote_pid, | |
235 | SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32); | |
236 | if (ret && ret != -EEXIST) { | |
237 | dev_err(dev, "failed to allocate glink descriptors\n"); | |
238 | goto err_put_dev; | |
239 | } | |
240 | ||
241 | descs = qcom_smem_get(remote_pid, | |
242 | SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size); | |
243 | if (IS_ERR(descs)) { | |
244 | dev_err(dev, "failed to acquire xprt descriptor\n"); | |
245 | ret = PTR_ERR(descs); | |
246 | goto err_put_dev; | |
247 | } | |
248 | ||
249 | if (size != 32) { | |
250 | dev_err(dev, "glink descriptor of invalid size\n"); | |
251 | ret = -EINVAL; | |
252 | goto err_put_dev; | |
253 | } | |
254 | ||
255 | tx_pipe->tail = &descs[0]; | |
256 | tx_pipe->head = &descs[1]; | |
257 | rx_pipe->tail = &descs[2]; | |
258 | rx_pipe->head = &descs[3]; | |
259 | ||
260 | ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, | |
261 | SZ_16K); | |
262 | if (ret && ret != -EEXIST) { | |
263 | dev_err(dev, "failed to allocate TX fifo\n"); | |
264 | goto err_put_dev; | |
265 | } | |
266 | ||
267 | tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0, | |
268 | &tx_pipe->native.length); | |
269 | if (IS_ERR(tx_pipe->fifo)) { | |
270 | dev_err(dev, "failed to acquire TX fifo\n"); | |
271 | ret = PTR_ERR(tx_pipe->fifo); | |
272 | goto err_put_dev; | |
273 | } | |
274 | ||
275 | rx_pipe->native.avail = glink_smem_rx_avail; | |
276 | rx_pipe->native.peak = glink_smem_rx_peak; | |
277 | rx_pipe->native.advance = glink_smem_rx_advance; | |
278 | rx_pipe->remote_pid = remote_pid; | |
279 | ||
280 | tx_pipe->native.avail = glink_smem_tx_avail; | |
281 | tx_pipe->native.write = glink_smem_tx_write; | |
282 | tx_pipe->remote_pid = remote_pid; | |
283 | ||
284 | *rx_pipe->tail = 0; | |
285 | *tx_pipe->head = 0; | |
286 | ||
287 | glink = qcom_glink_native_probe(dev, | |
933b45da S |
288 | GLINK_FEATURE_INTENT_REUSE, |
289 | &rx_pipe->native, &tx_pipe->native, | |
290 | false); | |
caf989c3 BA |
291 | if (IS_ERR(glink)) { |
292 | ret = PTR_ERR(glink); | |
293 | goto err_put_dev; | |
294 | } | |
295 | ||
296 | return glink; | |
297 | ||
298 | err_put_dev: | |
299 | put_device(dev); | |
300 | ||
301 | return ERR_PTR(ret); | |
302 | } | |
303 | EXPORT_SYMBOL_GPL(qcom_glink_smem_register); | |
304 | ||
305 | void qcom_glink_smem_unregister(struct qcom_glink *glink) | |
306 | { | |
307 | qcom_glink_native_remove(glink); | |
308 | qcom_glink_native_unregister(glink); | |
309 | } | |
310 | EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister); | |
311 | ||
312 | MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>"); | |
313 | MODULE_DESCRIPTION("Qualcomm GLINK SMEM driver"); | |
314 | MODULE_LICENSE("GPL v2"); |