xref: /linux/drivers/rpmsg/qcom_glink_smem.c (revision 4623e8bf1de0b86e23a56cdb39a72f054e89c3bd)
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 
36caf989c3SBjorn Andersson struct glink_smem_pipe {
37caf989c3SBjorn Andersson 	struct qcom_glink_pipe native;
38caf989c3SBjorn Andersson 
39caf989c3SBjorn Andersson 	__le32 *tail;
40caf989c3SBjorn Andersson 	__le32 *head;
41caf989c3SBjorn Andersson 
42caf989c3SBjorn Andersson 	void *fifo;
43caf989c3SBjorn Andersson 
44caf989c3SBjorn Andersson 	int remote_pid;
45caf989c3SBjorn Andersson };
46caf989c3SBjorn Andersson 
47caf989c3SBjorn Andersson #define to_smem_pipe(p) container_of(p, struct glink_smem_pipe, native)
48caf989c3SBjorn Andersson 
49caf989c3SBjorn Andersson static size_t glink_smem_rx_avail(struct qcom_glink_pipe *np)
50caf989c3SBjorn Andersson {
51caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
52caf989c3SBjorn Andersson 	size_t len;
53caf989c3SBjorn Andersson 	void *fifo;
54caf989c3SBjorn Andersson 	u32 head;
55caf989c3SBjorn Andersson 	u32 tail;
56caf989c3SBjorn Andersson 
57caf989c3SBjorn Andersson 	if (!pipe->fifo) {
58caf989c3SBjorn Andersson 		fifo = qcom_smem_get(pipe->remote_pid,
59caf989c3SBjorn Andersson 				     SMEM_GLINK_NATIVE_XPRT_FIFO_1, &len);
60caf989c3SBjorn Andersson 		if (IS_ERR(fifo)) {
61caf989c3SBjorn Andersson 			pr_err("failed to acquire RX fifo handle: %ld\n",
62caf989c3SBjorn Andersson 			       PTR_ERR(fifo));
63caf989c3SBjorn Andersson 			return 0;
64caf989c3SBjorn Andersson 		}
65caf989c3SBjorn Andersson 
66caf989c3SBjorn Andersson 		pipe->fifo = fifo;
67caf989c3SBjorn Andersson 		pipe->native.length = len;
68caf989c3SBjorn Andersson 	}
69caf989c3SBjorn Andersson 
70caf989c3SBjorn Andersson 	head = le32_to_cpu(*pipe->head);
71caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
72caf989c3SBjorn Andersson 
73caf989c3SBjorn Andersson 	if (head < tail)
74caf989c3SBjorn Andersson 		return pipe->native.length - tail + head;
75caf989c3SBjorn Andersson 	else
76caf989c3SBjorn Andersson 		return head - tail;
77caf989c3SBjorn Andersson }
78caf989c3SBjorn Andersson 
79caf989c3SBjorn Andersson static void glink_smem_rx_peak(struct qcom_glink_pipe *np,
80b88eee97SBjorn Andersson 			       void *data, unsigned int offset, size_t count)
81caf989c3SBjorn Andersson {
82caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
83caf989c3SBjorn Andersson 	size_t len;
84caf989c3SBjorn Andersson 	u32 tail;
85caf989c3SBjorn Andersson 
86caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
87b88eee97SBjorn Andersson 	tail += offset;
88b88eee97SBjorn Andersson 	if (tail >= pipe->native.length)
89b88eee97SBjorn Andersson 		tail -= pipe->native.length;
90caf989c3SBjorn Andersson 
91caf989c3SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - tail);
92928002a5SArun Kumar Neelakantam 	if (len)
93928002a5SArun Kumar Neelakantam 		memcpy_fromio(data, pipe->fifo + tail, len);
94caf989c3SBjorn Andersson 
95928002a5SArun Kumar Neelakantam 	if (len != count)
96928002a5SArun Kumar Neelakantam 		memcpy_fromio(data + len, pipe->fifo, (count - len));
97caf989c3SBjorn Andersson }
98caf989c3SBjorn Andersson 
99caf989c3SBjorn Andersson static void glink_smem_rx_advance(struct qcom_glink_pipe *np,
100caf989c3SBjorn Andersson 				  size_t count)
101caf989c3SBjorn Andersson {
102caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
103caf989c3SBjorn Andersson 	u32 tail;
104caf989c3SBjorn Andersson 
105caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
106caf989c3SBjorn Andersson 
107caf989c3SBjorn Andersson 	tail += count;
108*4623e8bfSChris Lew 	if (tail >= pipe->native.length)
109caf989c3SBjorn Andersson 		tail -= pipe->native.length;
110caf989c3SBjorn Andersson 
111caf989c3SBjorn Andersson 	*pipe->tail = cpu_to_le32(tail);
112caf989c3SBjorn Andersson }
113caf989c3SBjorn Andersson 
114caf989c3SBjorn Andersson static size_t glink_smem_tx_avail(struct qcom_glink_pipe *np)
115caf989c3SBjorn Andersson {
116caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(np);
117caf989c3SBjorn Andersson 	u32 head;
118caf989c3SBjorn Andersson 	u32 tail;
119caf989c3SBjorn Andersson 	u32 avail;
120caf989c3SBjorn Andersson 
121caf989c3SBjorn Andersson 	head = le32_to_cpu(*pipe->head);
122caf989c3SBjorn Andersson 	tail = le32_to_cpu(*pipe->tail);
123caf989c3SBjorn Andersson 
124caf989c3SBjorn Andersson 	if (tail <= head)
125caf989c3SBjorn Andersson 		avail = pipe->native.length - head + tail;
126caf989c3SBjorn Andersson 	else
127caf989c3SBjorn Andersson 		avail = tail - head;
128caf989c3SBjorn Andersson 
129caf989c3SBjorn Andersson 	if (avail < (FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE))
130caf989c3SBjorn Andersson 		avail = 0;
131caf989c3SBjorn Andersson 	else
132caf989c3SBjorn Andersson 		avail -= FIFO_FULL_RESERVE + TX_BLOCKED_CMD_RESERVE;
133caf989c3SBjorn Andersson 
134caf989c3SBjorn Andersson 	return avail;
135caf989c3SBjorn Andersson }
136caf989c3SBjorn Andersson 
137caf989c3SBjorn Andersson static unsigned int glink_smem_tx_write_one(struct glink_smem_pipe *pipe,
138caf989c3SBjorn Andersson 					    unsigned int head,
139caf989c3SBjorn Andersson 					    const void *data, size_t count)
140caf989c3SBjorn Andersson {
141caf989c3SBjorn Andersson 	size_t len;
142caf989c3SBjorn Andersson 
143caf989c3SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - head);
144caf989c3SBjorn Andersson 	if (len)
145caf989c3SBjorn Andersson 		memcpy(pipe->fifo + head, data, len);
146caf989c3SBjorn Andersson 
147caf989c3SBjorn Andersson 	if (len != count)
148caf989c3SBjorn Andersson 		memcpy(pipe->fifo, data + len, count - len);
149caf989c3SBjorn Andersson 
150caf989c3SBjorn Andersson 	head += count;
151caf989c3SBjorn Andersson 	if (head >= pipe->native.length)
152caf989c3SBjorn Andersson 		head -= pipe->native.length;
153caf989c3SBjorn Andersson 
154caf989c3SBjorn Andersson 	return head;
155caf989c3SBjorn Andersson }
156caf989c3SBjorn Andersson 
157caf989c3SBjorn Andersson static void glink_smem_tx_write(struct qcom_glink_pipe *glink_pipe,
158caf989c3SBjorn Andersson 				const void *hdr, size_t hlen,
159caf989c3SBjorn Andersson 				const void *data, size_t dlen)
160caf989c3SBjorn Andersson {
161caf989c3SBjorn Andersson 	struct glink_smem_pipe *pipe = to_smem_pipe(glink_pipe);
162caf989c3SBjorn Andersson 	unsigned int head;
163caf989c3SBjorn Andersson 
164caf989c3SBjorn Andersson 	head = le32_to_cpu(*pipe->head);
165caf989c3SBjorn Andersson 
166caf989c3SBjorn Andersson 	head = glink_smem_tx_write_one(pipe, head, hdr, hlen);
167caf989c3SBjorn Andersson 	head = glink_smem_tx_write_one(pipe, head, data, dlen);
168caf989c3SBjorn Andersson 
169caf989c3SBjorn Andersson 	/* Ensure head is always aligned to 8 bytes */
170caf989c3SBjorn Andersson 	head = ALIGN(head, 8);
171caf989c3SBjorn Andersson 	if (head >= pipe->native.length)
172caf989c3SBjorn Andersson 		head -= pipe->native.length;
173caf989c3SBjorn Andersson 
1749d324973SBjorn Andersson 	/* Ensure ordering of fifo and head update */
1759d324973SBjorn Andersson 	wmb();
1769d324973SBjorn Andersson 
177caf989c3SBjorn Andersson 	*pipe->head = cpu_to_le32(head);
178caf989c3SBjorn Andersson }
179caf989c3SBjorn Andersson 
180caf989c3SBjorn Andersson static void qcom_glink_smem_release(struct device *dev)
181caf989c3SBjorn Andersson {
182caf989c3SBjorn Andersson 	kfree(dev);
183caf989c3SBjorn Andersson }
184caf989c3SBjorn Andersson 
185caf989c3SBjorn Andersson struct qcom_glink *qcom_glink_smem_register(struct device *parent,
186caf989c3SBjorn Andersson 					    struct device_node *node)
187caf989c3SBjorn Andersson {
188caf989c3SBjorn Andersson 	struct glink_smem_pipe *rx_pipe;
189caf989c3SBjorn Andersson 	struct glink_smem_pipe *tx_pipe;
190caf989c3SBjorn Andersson 	struct qcom_glink *glink;
191caf989c3SBjorn Andersson 	struct device *dev;
192caf989c3SBjorn Andersson 	u32 remote_pid;
193caf989c3SBjorn Andersson 	__le32 *descs;
194caf989c3SBjorn Andersson 	size_t size;
195caf989c3SBjorn Andersson 	int ret;
196caf989c3SBjorn Andersson 
197caf989c3SBjorn Andersson 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
198caf989c3SBjorn Andersson 	if (!dev)
199caf989c3SBjorn Andersson 		return ERR_PTR(-ENOMEM);
200caf989c3SBjorn Andersson 
201caf989c3SBjorn Andersson 	dev->parent = parent;
202caf989c3SBjorn Andersson 	dev->of_node = node;
203caf989c3SBjorn Andersson 	dev->release = qcom_glink_smem_release;
2049fe69a72SBjorn Andersson 	dev_set_name(dev, "%s:%pOFn", dev_name(parent->parent), node);
205caf989c3SBjorn Andersson 	ret = device_register(dev);
206caf989c3SBjorn Andersson 	if (ret) {
207caf989c3SBjorn Andersson 		pr_err("failed to register glink edge\n");
208a9011726SArvind Yadav 		put_device(dev);
209caf989c3SBjorn Andersson 		return ERR_PTR(ret);
210caf989c3SBjorn Andersson 	}
211caf989c3SBjorn Andersson 
212caf989c3SBjorn Andersson 	ret = of_property_read_u32(dev->of_node, "qcom,remote-pid",
213caf989c3SBjorn Andersson 				   &remote_pid);
214caf989c3SBjorn Andersson 	if (ret) {
215caf989c3SBjorn Andersson 		dev_err(dev, "failed to parse qcom,remote-pid\n");
216caf989c3SBjorn Andersson 		goto err_put_dev;
217caf989c3SBjorn Andersson 	}
218caf989c3SBjorn Andersson 
219caf989c3SBjorn Andersson 	rx_pipe = devm_kzalloc(dev, sizeof(*rx_pipe), GFP_KERNEL);
220caf989c3SBjorn Andersson 	tx_pipe = devm_kzalloc(dev, sizeof(*tx_pipe), GFP_KERNEL);
221caf989c3SBjorn Andersson 	if (!rx_pipe || !tx_pipe) {
222caf989c3SBjorn Andersson 		ret = -ENOMEM;
223caf989c3SBjorn Andersson 		goto err_put_dev;
224caf989c3SBjorn Andersson 	}
225caf989c3SBjorn Andersson 
226caf989c3SBjorn Andersson 	ret = qcom_smem_alloc(remote_pid,
227caf989c3SBjorn Andersson 			      SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, 32);
228caf989c3SBjorn Andersson 	if (ret && ret != -EEXIST) {
229caf989c3SBjorn Andersson 		dev_err(dev, "failed to allocate glink descriptors\n");
230caf989c3SBjorn Andersson 		goto err_put_dev;
231caf989c3SBjorn Andersson 	}
232caf989c3SBjorn Andersson 
233caf989c3SBjorn Andersson 	descs = qcom_smem_get(remote_pid,
234caf989c3SBjorn Andersson 			      SMEM_GLINK_NATIVE_XPRT_DESCRIPTOR, &size);
235caf989c3SBjorn Andersson 	if (IS_ERR(descs)) {
236caf989c3SBjorn Andersson 		dev_err(dev, "failed to acquire xprt descriptor\n");
237caf989c3SBjorn Andersson 		ret = PTR_ERR(descs);
238caf989c3SBjorn Andersson 		goto err_put_dev;
239caf989c3SBjorn Andersson 	}
240caf989c3SBjorn Andersson 
241caf989c3SBjorn Andersson 	if (size != 32) {
242caf989c3SBjorn Andersson 		dev_err(dev, "glink descriptor of invalid size\n");
243caf989c3SBjorn Andersson 		ret = -EINVAL;
244caf989c3SBjorn Andersson 		goto err_put_dev;
245caf989c3SBjorn Andersson 	}
246caf989c3SBjorn Andersson 
247caf989c3SBjorn Andersson 	tx_pipe->tail = &descs[0];
248caf989c3SBjorn Andersson 	tx_pipe->head = &descs[1];
249caf989c3SBjorn Andersson 	rx_pipe->tail = &descs[2];
250caf989c3SBjorn Andersson 	rx_pipe->head = &descs[3];
251caf989c3SBjorn Andersson 
252caf989c3SBjorn Andersson 	ret = qcom_smem_alloc(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0,
253caf989c3SBjorn Andersson 			      SZ_16K);
254caf989c3SBjorn Andersson 	if (ret && ret != -EEXIST) {
255caf989c3SBjorn Andersson 		dev_err(dev, "failed to allocate TX fifo\n");
256caf989c3SBjorn Andersson 		goto err_put_dev;
257caf989c3SBjorn Andersson 	}
258caf989c3SBjorn Andersson 
259caf989c3SBjorn Andersson 	tx_pipe->fifo = qcom_smem_get(remote_pid, SMEM_GLINK_NATIVE_XPRT_FIFO_0,
260caf989c3SBjorn Andersson 				      &tx_pipe->native.length);
261caf989c3SBjorn Andersson 	if (IS_ERR(tx_pipe->fifo)) {
262caf989c3SBjorn Andersson 		dev_err(dev, "failed to acquire TX fifo\n");
263caf989c3SBjorn Andersson 		ret = PTR_ERR(tx_pipe->fifo);
264caf989c3SBjorn Andersson 		goto err_put_dev;
265caf989c3SBjorn Andersson 	}
266caf989c3SBjorn Andersson 
267caf989c3SBjorn Andersson 	rx_pipe->native.avail = glink_smem_rx_avail;
268caf989c3SBjorn Andersson 	rx_pipe->native.peak = glink_smem_rx_peak;
269caf989c3SBjorn Andersson 	rx_pipe->native.advance = glink_smem_rx_advance;
270caf989c3SBjorn Andersson 	rx_pipe->remote_pid = remote_pid;
271caf989c3SBjorn Andersson 
272caf989c3SBjorn Andersson 	tx_pipe->native.avail = glink_smem_tx_avail;
273caf989c3SBjorn Andersson 	tx_pipe->native.write = glink_smem_tx_write;
274caf989c3SBjorn Andersson 	tx_pipe->remote_pid = remote_pid;
275caf989c3SBjorn Andersson 
276caf989c3SBjorn Andersson 	*rx_pipe->tail = 0;
277caf989c3SBjorn Andersson 	*tx_pipe->head = 0;
278caf989c3SBjorn Andersson 
279caf989c3SBjorn Andersson 	glink = qcom_glink_native_probe(dev,
280933b45daSSricharan R 					GLINK_FEATURE_INTENT_REUSE,
281933b45daSSricharan R 					&rx_pipe->native, &tx_pipe->native,
282933b45daSSricharan R 					false);
283caf989c3SBjorn Andersson 	if (IS_ERR(glink)) {
284caf989c3SBjorn Andersson 		ret = PTR_ERR(glink);
285caf989c3SBjorn Andersson 		goto err_put_dev;
286caf989c3SBjorn Andersson 	}
287caf989c3SBjorn Andersson 
288caf989c3SBjorn Andersson 	return glink;
289caf989c3SBjorn Andersson 
290caf989c3SBjorn Andersson err_put_dev:
291a9011726SArvind Yadav 	device_unregister(dev);
292caf989c3SBjorn Andersson 
293caf989c3SBjorn Andersson 	return ERR_PTR(ret);
294caf989c3SBjorn Andersson }
295caf989c3SBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_smem_register);
296caf989c3SBjorn Andersson 
297caf989c3SBjorn Andersson void qcom_glink_smem_unregister(struct qcom_glink *glink)
298caf989c3SBjorn Andersson {
299caf989c3SBjorn Andersson 	qcom_glink_native_remove(glink);
300caf989c3SBjorn Andersson 	qcom_glink_native_unregister(glink);
301caf989c3SBjorn Andersson }
302caf989c3SBjorn Andersson EXPORT_SYMBOL_GPL(qcom_glink_smem_unregister);
303caf989c3SBjorn Andersson 
304caf989c3SBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
305caf989c3SBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK SMEM driver");
306caf989c3SBjorn Andersson MODULE_LICENSE("GPL v2");
307