xref: /linux/drivers/rpmsg/qcom_glink_smem.c (revision ab9fdd41d970c38ddc0fd59e5f8f37e8d966d454)
184369fbeSSuman Anna // SPDX-License-Identifier: GPL-2.0
2caf989c3SBjorn Andersson /*
3caf989c3SBjorn Andersson  * Copyright (c) 2016, Linaro Ltd
4caf989c3SBjorn Andersson  */
5caf989c3SBjorn Andersson 
6caf989c3SBjorn Andersson #include <linux/io.h>
7caf989c3SBjorn Andersson #include <linux/module.h>
8caf989c3SBjorn Andersson #include <linux/of.h>
9caf989c3SBjorn Andersson #include <linux/of_address.h>
10caf989c3SBjorn Andersson #include <linux/interrupt.h>
11caf989c3SBjorn Andersson #include <linux/platform_device.h>
12caf989c3SBjorn Andersson #include <linux/mfd/syscon.h>
13caf989c3SBjorn Andersson #include <linux/slab.h>
14caf989c3SBjorn Andersson #include <linux/rpmsg.h>
15caf989c3SBjorn Andersson #include <linux/idr.h>
16caf989c3SBjorn Andersson #include <linux/circ_buf.h>
17caf989c3SBjorn Andersson #include <linux/soc/qcom/smem.h>
18caf989c3SBjorn Andersson #include <linux/sizes.h>
19caf989c3SBjorn Andersson #include <linux/delay.h>
20caf989c3SBjorn Andersson #include <linux/regmap.h>
21caf989c3SBjorn Andersson #include <linux/workqueue.h>
22caf989c3SBjorn Andersson #include <linux/list.h>
23caf989c3SBjorn Andersson 
24caf989c3SBjorn Andersson #include <linux/rpmsg/qcom_glink.h>
25caf989c3SBjorn Andersson 
26caf989c3SBjorn Andersson #include "qcom_glink_native.h"
27caf989c3SBjorn Andersson 
28caf989c3SBjorn Andersson #define FIFO_FULL_RESERVE 8
29caf989c3SBjorn Andersson #define FIFO_ALIGNMENT 8
30caf989c3SBjorn Andersson #define TX_BLOCKED_CMD_RESERVE 8 /* size of struct read_notif_request */
31caf989c3SBjorn Andersson 
32caf989c3SBjorn Andersson #define SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR	478
33caf989c3SBjorn Andersson #define SMEM_GLINK_NATIVE_XPRT_FIFO_0		479
34caf989c3SBjorn Andersson #define SMEM_GLINK_NATIVE_XPRT_FIFO_1		480
35caf989c3SBjorn Andersson 
36*ab9fdd41SBjorn Andersson struct qcom_glink_smem {
37*ab9fdd41SBjorn Andersson 	struct device dev;
38*ab9fdd41SBjorn Andersson 
39*ab9fdd41SBjorn Andersson 	struct qcom_glink *glink;
40*ab9fdd41SBjorn Andersson 
41*ab9fdd41SBjorn Andersson 	u32 remote_pid;
42*ab9fdd41SBjorn Andersson };
43*ab9fdd41SBjorn Andersson 
44caf989c3SBjorn Andersson struct glink_smem_pipe {
45caf989c3SBjorn Andersson 	struct qcom_glink_pipe native;
46caf989c3SBjorn Andersson 
47caf989c3SBjorn Andersson 	__le32 *tail;
48caf989c3SBjorn Andersson 	__le32 *head;
49caf989c3SBjorn Andersson 
50caf989c3SBjorn Andersson 	void *fifo;
51caf989c3SBjorn Andersson 
52*ab9fdd41SBjorn Andersson 	struct qcom_glink_smem *smem;
53caf989c3SBjorn Andersson };
54caf989c3SBjorn Andersson 
55caf989c3SBjorn Andersson #define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native)
56caf989c3SBjorn Andersson 
57caf989c3SBjorn Andersson static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
58caf989c3SBjorn Andersson {
59caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
60*ab9fdd41SBjorn Andersson 	struct qcom_glink_smem *smem = pipe->smem;
61caf989c3SBjorn Andersson 	size_t len;
62caf989c3SBjorn Andersson 	void *fifo;
63caf989c3SBjorn Andersson 	u32 head;
64caf989c3SBjorn Andersson 	u32 tail;
65caf989c3SBjorn Andersson 
66caf989c3SBjorn Andersson 	if (!pipe->fifo) {
67*ab9fdd41SBjorn Andersson 		fifo = qcom_smem_get(smem->remote_pid,
68caf989c3SBjorn Andersson 				     SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len);
69caf989c3SBjorn Andersson 		if (IS_ERR(fifo)) {
70caf989c3SBjorn Andersson 			pr_err("failed to acquire RX fifo handle: %ld\n",
71caf989c3SBjorn Andersson 			       PTR_ERR(fifo));
72caf989c3SBjorn Andersson 			return 0;
73caf989c3SBjorn Andersson 		}
74caf989c3SBjorn Andersson 
75caf989c3SBjorn Andersson 		pipe->fifo = fifo;
76caf989c3SBjorn Andersson 		pipe->native.length = len;
77caf989c3SBjorn Andersson 	}
78caf989c3SBjorn Andersson 
79caf989c3SBjorn Andersson 	head = le32_to_cpu(*pipe->head);
80caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
81caf989c3SBjorn Andersson 
82caf989c3SBjorn Andersson 	if (head < tail)
83caf989c3SBjorn Andersson 		return pipe->native.length - tail + head;
84caf989c3SBjorn Andersson 	else
85caf989c3SBjorn Andersson 		return head - tail;
86caf989c3SBjorn Andersson }
87caf989c3SBjorn Andersson 
88caf989c3SBjorn Andersson static void glink_smem_rx_peak(struct qcom_glink_pipe *np,
89b88eee97SBjorn Andersson 			       void *data, unsigned int offset, size_t count)
90caf989c3SBjorn Andersson {
91caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
92caf989c3SBjorn Andersson 	size_t len;
93caf989c3SBjorn Andersson 	u32 tail;
94caf989c3SBjorn Andersson 
95caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
96b88eee97SBjorn Andersson 	tail += offset;
97b88eee97SBjorn Andersson 	if (tail >= pipe->native.length)
98b88eee97SBjorn Andersson 		tail -= pipe->native.length;
99caf989c3SBjorn Andersson 
100caf989c3SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - tail);
101928002a5SArun Kumar Neelakantam 	if (len)
102928002a5SArun Kumar Neelakantam 		memcpy_fromio(data, pipe->fifo + tail, len);
103caf989c3SBjorn Andersson 
104928002a5SArun Kumar Neelakantam 	if (len != count)
105928002a5SArun Kumar Neelakantam 		memcpy_fromio(data + len, pipe->fifo, (count - len));
106caf989c3SBjorn Andersson }
107caf989c3SBjorn Andersson 
108caf989c3SBjorn Andersson static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
109caf989c3SBjorn Andersson 				  size_t count)
110caf989c3SBjorn Andersson {
111caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
112caf989c3SBjorn Andersson 	u32 tail;
113caf989c3SBjorn Andersson 
114caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
115caf989c3SBjorn Andersson 
116caf989c3SBjorn Andersson 	tail += count;
1174623e8bfSChris Lew 	if (tail >= pipe->native.length)
118caf989c3SBjorn Andersson 		tail -= pipe->native.length;
119caf989c3SBjorn Andersson 
120caf989c3SBjorn Andersson 	*pipe->tail = cpu_to_le32(tail);
121caf989c3SBjorn Andersson }
122caf989c3SBjorn Andersson 
123caf989c3SBjorn Andersson static size_t glink_smem_tx_avail(struct qcom_glink_pipe *np)
124caf989c3SBjorn Andersson {
125caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
126caf989c3SBjorn Andersson 	u32 head;
127caf989c3SBjorn Andersson 	u32 tail;
128caf989c3SBjorn Andersson 	u32 avail;
129caf989c3SBjorn Andersson 
130caf989c3SBjorn Andersson 	head = le32_to_cpu(*pipe->head);
131caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
132caf989c3SBjorn Andersson 
133caf989c3SBjorn Andersson 	if (tail <= head)
134caf989c3SBjorn Andersson 		avail = pipe->native.length - head + tail;
135caf989c3SBjorn Andersson 	else
136caf989c3SBjorn Andersson 		avail = tail - head;
137caf989c3SBjorn Andersson 
138caf989c3SBjorn Andersson 	if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE))
139caf989c3SBjorn Andersson 		avail = 0;
140caf989c3SBjorn Andersson 	else
141caf989c3SBjorn Andersson 		avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE;
142caf989c3SBjorn Andersson 
143caf989c3SBjorn Andersson 	return avail;
144caf989c3SBjorn Andersson }
145caf989c3SBjorn Andersson 
146caf989c3SBjorn Andersson static unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe,
147caf989c3SBjorn Andersson 					    unsigned int head,
148caf989c3SBjorn Andersson 					    const void *data, size_t count)
149caf989c3SBjorn Andersson {
150caf989c3SBjorn Andersson 	size_t len;
151caf989c3SBjorn Andersson 
152caf989c3SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - head);
153caf989c3SBjorn Andersson 	if (len)
154caf989c3SBjorn Andersson 		memcpy(pipe->fifo + head, data, len);
155caf989c3SBjorn Andersson 
156caf989c3SBjorn Andersson 	if (len != count)
157caf989c3SBjorn Andersson 		memcpy(pipe->fifo, data + len, count - len);
158caf989c3SBjorn Andersson 
159caf989c3SBjorn Andersson 	head += count;
160caf989c3SBjorn Andersson 	if (head >= pipe->native.length)
161caf989c3SBjorn Andersson 		head -= pipe->native.length;
162caf989c3SBjorn Andersson 
163caf989c3SBjorn Andersson 	return head;
164caf989c3SBjorn Andersson }
165caf989c3SBjorn Andersson 
166caf989c3SBjorn Andersson static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe,
167caf989c3SBjorn Andersson 				const void *hdr, size_t hlen,
168caf989c3SBjorn Andersson 				const void *data, size_t dlen)
169caf989c3SBjorn Andersson {
170caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe);
171caf989c3SBjorn Andersson 	unsigned int head;
172caf989c3SBjorn Andersson 
173caf989c3SBjorn Andersson 	head = le32_to_cpu(*pipe->head);
174caf989c3SBjorn Andersson 
175caf989c3SBjorn Andersson 	head = glink_smem_tx_write_one(pipe, head, hdr, hlen);
176caf989c3SBjorn Andersson 	head = glink_smem_tx_write_one(pipe, head, data, dlen);
177caf989c3SBjorn Andersson 
178caf989c3SBjorn Andersson 	/* Ensure head is always aligned to 8 bytes */
179caf989c3SBjorn Andersson 	head = ALIGN(head, 8);
180caf989c3SBjorn Andersson 	if (head >= pipe->native.length)
181caf989c3SBjorn Andersson 		head -= pipe->native.length;
182caf989c3SBjorn Andersson 
1839d324973SBjorn Andersson 	/* Ensure ordering of fifo and head update */
1849d324973SBjorn Andersson 	wmb();
1859d324973SBjorn Andersson 
186caf989c3SBjorn Andersson 	*pipe->head = cpu_to_le32(head);
187caf989c3SBjorn Andersson }
188caf989c3SBjorn Andersson 
189caf989c3SBjorn Andersson static void qcom_glink_smem_release(struct device *dev)
190caf989c3SBjorn Andersson {
191*ab9fdd41SBjorn Andersson 	struct qcom_glink_smem *smem = container_of(dev, struct qcom_glink_smem, dev);
192*ab9fdd41SBjorn Andersson 
193*ab9fdd41SBjorn Andersson 	kfree(smem);
194caf989c3SBjorn Andersson }
195caf989c3SBjorn Andersson 
196*ab9fdd41SBjorn Andersson struct qcom_glink_smem *qcom_glink_smem_register(struct device *parent,
197caf989c3SBjorn Andersson 						 struct device_node *node)
198caf989c3SBjorn Andersson {
199caf989c3SBjorn Andersson 	struct glink_smem_pipe *rx_pipe;
200caf989c3SBjorn Andersson 	struct glink_smem_pipe *tx_pipe;
201*ab9fdd41SBjorn Andersson 	struct qcom_glink_smem *smem;
202caf989c3SBjorn Andersson 	struct qcom_glink *glink;
203caf989c3SBjorn Andersson 	struct device *dev;
204caf989c3SBjorn Andersson 	u32 remote_pid;
205caf989c3SBjorn Andersson 	__le32 *descs;
206caf989c3SBjorn Andersson 	size_t size;
207caf989c3SBjorn Andersson 	int ret;
208caf989c3SBjorn Andersson 
209*ab9fdd41SBjorn Andersson 	smem = kzalloc(sizeof(*smem), GFP_KERNEL);
210*ab9fdd41SBjorn Andersson 	if (!smem)
211caf989c3SBjorn Andersson 		return ERR_PTR(-ENOMEM);
212caf989c3SBjorn Andersson 
213*ab9fdd41SBjorn Andersson 	dev = &smem->dev;
214*ab9fdd41SBjorn Andersson 
215caf989c3SBjorn Andersson 	dev->parent = parent;
216caf989c3SBjorn Andersson 	dev->of_node = node;
217caf989c3SBjorn Andersson 	dev->release = qcom_glink_smem_release;
2189fe69a72SBjorn Andersson 	dev_set_name(dev, "%s:%pOFn", dev_name(parent->parent), node);
219caf989c3SBjorn Andersson 	ret = device_register(dev);
220caf989c3SBjorn Andersson 	if (ret) {
221caf989c3SBjorn Andersson 		pr_err("failed to register glink edge\n");
222a9011726SArvind Yadav 		put_device(dev);
223caf989c3SBjorn Andersson 		return ERR_PTR(ret);
224caf989c3SBjorn Andersson 	}
225caf989c3SBjorn Andersson 
226caf989c3SBjorn Andersson 	ret = of_property_read_u32(dev->of_node, "qcom,remote-pid",
227caf989c3SBjorn Andersson 				   &remote_pid);
228caf989c3SBjorn Andersson 	if (ret) {
229caf989c3SBjorn Andersson 		dev_err(dev, "failed to parse qcom,remote-pid\n");
230caf989c3SBjorn Andersson 		goto err_put_dev;
231caf989c3SBjorn Andersson 	}
232caf989c3SBjorn Andersson 
233*ab9fdd41SBjorn Andersson 	smem->remote_pid = remote_pid;
234*ab9fdd41SBjorn Andersson 
235caf989c3SBjorn Andersson 	rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL);
236caf989c3SBjorn Andersson 	tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL);
237caf989c3SBjorn Andersson 	if (!rx_pipe || !tx_pipe) {
238caf989c3SBjorn Andersson 		ret = -ENOMEM;
239caf989c3SBjorn Andersson 		goto err_put_dev;
240caf989c3SBjorn Andersson 	}
241caf989c3SBjorn Andersson 
242caf989c3SBjorn Andersson 	ret = qcom_smem_alloc(remote_pid,
243caf989c3SBjorn Andersson 			      SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32);
244caf989c3SBjorn Andersson 	if (ret && ret != -EEXIST) {
245caf989c3SBjorn Andersson 		dev_err(dev, "failed to allocate glink descriptors\n");
246caf989c3SBjorn Andersson 		goto err_put_dev;
247caf989c3SBjorn Andersson 	}
248caf989c3SBjorn Andersson 
249caf989c3SBjorn Andersson 	descs = qcom_smem_get(remote_pid,
250caf989c3SBjorn Andersson 			      SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size);
251caf989c3SBjorn Andersson 	if (IS_ERR(descs)) {
252caf989c3SBjorn Andersson 		dev_err(dev, "failed to acquire xprt descriptor\n");
253caf989c3SBjorn Andersson 		ret = PTR_ERR(descs);
254caf989c3SBjorn Andersson 		goto err_put_dev;
255caf989c3SBjorn Andersson 	}
256caf989c3SBjorn Andersson 
257caf989c3SBjorn Andersson 	if (size != 32) {
258caf989c3SBjorn Andersson 		dev_err(dev, "glink descriptor of invalid size\n");
259caf989c3SBjorn Andersson 		ret = -EINVAL;
260caf989c3SBjorn Andersson 		goto err_put_dev;
261caf989c3SBjorn Andersson 	}
262caf989c3SBjorn Andersson 
263caf989c3SBjorn Andersson 	tx_pipe->tail = &descs[0];
264caf989c3SBjorn Andersson 	tx_pipe->head = &descs[1];
265caf989c3SBjorn Andersson 	rx_pipe->tail = &descs[2];
266caf989c3SBjorn Andersson 	rx_pipe->head = &descs[3];
267caf989c3SBjorn Andersson 
268caf989c3SBjorn Andersson 	ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0,
269caf989c3SBjorn Andersson 			      SZ_16K);
270caf989c3SBjorn Andersson 	if (ret && ret != -EEXIST) {
271caf989c3SBjorn Andersson 		dev_err(dev, "failed to allocate TX fifo\n");
272caf989c3SBjorn Andersson 		goto err_put_dev;
273caf989c3SBjorn Andersson 	}
274caf989c3SBjorn Andersson 
275caf989c3SBjorn Andersson 	tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0,
276caf989c3SBjorn Andersson 				      &tx_pipe->native.length);
277caf989c3SBjorn Andersson 	if (IS_ERR(tx_pipe->fifo)) {
278caf989c3SBjorn Andersson 		dev_err(dev, "failed to acquire TX fifo\n");
279caf989c3SBjorn Andersson 		ret = PTR_ERR(tx_pipe->fifo);
280caf989c3SBjorn Andersson 		goto err_put_dev;
281caf989c3SBjorn Andersson 	}
282caf989c3SBjorn Andersson 
283*ab9fdd41SBjorn Andersson 	rx_pipe->smem = smem;
284caf989c3SBjorn Andersson 	rx_pipe->native.avail = glink_smem_rx_avail;
285caf989c3SBjorn Andersson 	rx_pipe->native.peak = glink_smem_rx_peak;
286caf989c3SBjorn Andersson 	rx_pipe->native.advance = glink_smem_rx_advance;
287caf989c3SBjorn Andersson 
288*ab9fdd41SBjorn Andersson 	tx_pipe->smem = smem;
289caf989c3SBjorn Andersson 	tx_pipe->native.avail = glink_smem_tx_avail;
290caf989c3SBjorn Andersson 	tx_pipe->native.write = glink_smem_tx_write;
291caf989c3SBjorn Andersson 
292caf989c3SBjorn Andersson 	*rx_pipe->tail = 0;
293caf989c3SBjorn Andersson 	*tx_pipe->head = 0;
294caf989c3SBjorn Andersson 
295caf989c3SBjorn Andersson 	glink = qcom_glink_native_probe(dev,
296933b45daSSricharan R 					GLINK_FEATURE_INTENT_REUSE,
297933b45daSSricharan R 					&rx_pipe->native, &tx_pipe->native,
298933b45daSSricharan R 					false);
299caf989c3SBjorn Andersson 	if (IS_ERR(glink)) {
300caf989c3SBjorn Andersson 		ret = PTR_ERR(glink);
301caf989c3SBjorn Andersson 		goto err_put_dev;
302caf989c3SBjorn Andersson 	}
303caf989c3SBjorn Andersson 
304*ab9fdd41SBjorn Andersson 	smem->glink = glink;
305*ab9fdd41SBjorn Andersson 
306*ab9fdd41SBjorn Andersson 	return smem;
307*ab9fdd41SBjorn Andersson 
308caf989c3SBjorn Andersson 
309caf989c3SBjorn Andersson err_put_dev:
310a9011726SArvind Yadav 	device_unregister(dev);
311caf989c3SBjorn Andersson 
312caf989c3SBjorn Andersson 	return ERR_PTR(ret);
313caf989c3SBjorn Andersson }
314caf989c3SBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_smem_register);
315caf989c3SBjorn Andersson 
316*ab9fdd41SBjorn Andersson void qcom_glink_smem_unregister(struct qcom_glink_smem *smem)
317caf989c3SBjorn Andersson {
318*ab9fdd41SBjorn Andersson 	struct qcom_glink *glink = smem->glink;
319*ab9fdd41SBjorn Andersson 
320caf989c3SBjorn Andersson 	qcom_glink_native_remove(glink);
321caf989c3SBjorn Andersson 	qcom_glink_native_unregister(glink);
322caf989c3SBjorn Andersson }
323caf989c3SBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister);
324caf989c3SBjorn Andersson 
325caf989c3SBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
326caf989c3SBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK SMEM driver");
327caf989c3SBjorn Andersson MODULE_LICENSE("GPL v2");
328