1b4f8e52bSBjorn Andersson /*
2b4f8e52bSBjorn Andersson  * Copyright (c) 2016-2017, Linaro Ltd
3b4f8e52bSBjorn Andersson  *
4b4f8e52bSBjorn Andersson  * This program is free software; you can redistribute it and/or modify
5b4f8e52bSBjorn Andersson  * it under the terms of the GNU General Public License version 2 and
6b4f8e52bSBjorn Andersson  * only version 2 as published by the Free Software Foundation.
7b4f8e52bSBjorn Andersson  *
8b4f8e52bSBjorn Andersson  * This program is distributed in the hope that it will be useful,
9b4f8e52bSBjorn Andersson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10b4f8e52bSBjorn Andersson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11b4f8e52bSBjorn Andersson  * GNU General Public License for more details.
12b4f8e52bSBjorn Andersson  */
13b4f8e52bSBjorn Andersson 
14b4f8e52bSBjorn Andersson #include <linux/idr.h>
15b4f8e52bSBjorn Andersson #include <linux/interrupt.h>
16b4f8e52bSBjorn Andersson #include <linux/io.h>
17b4f8e52bSBjorn Andersson #include <linux/list.h>
18b4f8e52bSBjorn Andersson #include <linux/mfd/syscon.h>
19b4f8e52bSBjorn Andersson #include <linux/module.h>
20b4f8e52bSBjorn Andersson #include <linux/of.h>
21b4f8e52bSBjorn Andersson #include <linux/of_address.h>
22b4f8e52bSBjorn Andersson #include <linux/platform_device.h>
23b4f8e52bSBjorn Andersson #include <linux/regmap.h>
24b4f8e52bSBjorn Andersson #include <linux/rpmsg.h>
25b4f8e52bSBjorn Andersson #include <linux/slab.h>
26b4f8e52bSBjorn Andersson #include <linux/workqueue.h>
27b4f8e52bSBjorn Andersson #include <linux/mailbox_client.h>
28b4f8e52bSBjorn Andersson 
29b4f8e52bSBjorn Andersson #include "rpmsg_internal.h"
30835764ddSBjorn Andersson #include "qcom_glink_native.h"
31b4f8e52bSBjorn Andersson 
32b4f8e52bSBjorn Andersson #define RPM_TOC_SIZE		256
33b4f8e52bSBjorn Andersson #define RPM_TOC_MAGIC		0x67727430 /* grt0 */
34b4f8e52bSBjorn Andersson #define RPM_TOC_MAX_ENTRIES	((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \
35b4f8e52bSBjorn Andersson 				 sizeof(struct rpm_toc_entry))
36b4f8e52bSBjorn Andersson 
37b4f8e52bSBjorn Andersson #define RPM_TX_FIFO_ID		0x61703272 /* ap2r */
38b4f8e52bSBjorn Andersson #define RPM_RX_FIFO_ID		0x72326170 /* r2ap */
39b4f8e52bSBjorn Andersson 
40e45c5dc2SBjorn Andersson #define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native)
41e45c5dc2SBjorn Andersson 
42b4f8e52bSBjorn Andersson struct rpm_toc_entry {
43b4f8e52bSBjorn Andersson 	__le32 id;
44b4f8e52bSBjorn Andersson 	__le32 offset;
45b4f8e52bSBjorn Andersson 	__le32 size;
46b4f8e52bSBjorn Andersson } __packed;
47b4f8e52bSBjorn Andersson 
48b4f8e52bSBjorn Andersson struct rpm_toc {
49b4f8e52bSBjorn Andersson 	__le32 magic;
50b4f8e52bSBjorn Andersson 	__le32 count;
51b4f8e52bSBjorn Andersson 
52b4f8e52bSBjorn Andersson 	struct rpm_toc_entry entries[];
53b4f8e52bSBjorn Andersson } __packed;
54b4f8e52bSBjorn Andersson 
55b4f8e52bSBjorn Andersson struct glink_rpm_pipe {
56e45c5dc2SBjorn Andersson 	struct qcom_glink_pipe native;
57e45c5dc2SBjorn Andersson 
58b4f8e52bSBjorn Andersson 	void __iomem *tail;
59b4f8e52bSBjorn Andersson 	void __iomem *head;
60b4f8e52bSBjorn Andersson 
61b4f8e52bSBjorn Andersson 	void __iomem *fifo;
62b4f8e52bSBjorn Andersson };
63b4f8e52bSBjorn Andersson 
64e45c5dc2SBjorn Andersson static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe)
65b4f8e52bSBjorn Andersson {
66e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
67b4f8e52bSBjorn Andersson 	unsigned int head;
68b4f8e52bSBjorn Andersson 	unsigned int tail;
69b4f8e52bSBjorn Andersson 
70b4f8e52bSBjorn Andersson 	head = readl(pipe->head);
71b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
72b4f8e52bSBjorn Andersson 
73b4f8e52bSBjorn Andersson 	if (head < tail)
74e45c5dc2SBjorn Andersson 		return pipe->native.length - tail + head;
75b4f8e52bSBjorn Andersson 	else
76b4f8e52bSBjorn Andersson 		return head - tail;
77b4f8e52bSBjorn Andersson }
78b4f8e52bSBjorn Andersson 
79e45c5dc2SBjorn Andersson static void glink_rpm_rx_peak(struct qcom_glink_pipe *glink_pipe,
80b4f8e52bSBjorn Andersson 			      void *data, size_t count)
81b4f8e52bSBjorn Andersson {
82e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
83b4f8e52bSBjorn Andersson 	unsigned int tail;
84b4f8e52bSBjorn Andersson 	size_t len;
85b4f8e52bSBjorn Andersson 
86b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
87b4f8e52bSBjorn Andersson 
88e45c5dc2SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - tail);
89b4f8e52bSBjorn Andersson 	if (len) {
90b4f8e52bSBjorn Andersson 		__ioread32_copy(data, pipe->fifo + tail,
91b4f8e52bSBjorn Andersson 				len / sizeof(u32));
92b4f8e52bSBjorn Andersson 	}
93b4f8e52bSBjorn Andersson 
94b4f8e52bSBjorn Andersson 	if (len != count) {
95b4f8e52bSBjorn Andersson 		__ioread32_copy(data + len, pipe->fifo,
96b4f8e52bSBjorn Andersson 				(count - len) / sizeof(u32));
97b4f8e52bSBjorn Andersson 	}
98b4f8e52bSBjorn Andersson }
99b4f8e52bSBjorn Andersson 
100e45c5dc2SBjorn Andersson static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe,
101b4f8e52bSBjorn Andersson 				 size_t count)
102b4f8e52bSBjorn Andersson {
103e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
104b4f8e52bSBjorn Andersson 	unsigned int tail;
105b4f8e52bSBjorn Andersson 
106b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
107b4f8e52bSBjorn Andersson 
108b4f8e52bSBjorn Andersson 	tail += count;
109e45c5dc2SBjorn Andersson 	if (tail >= pipe->native.length)
110e45c5dc2SBjorn Andersson 		tail -= pipe->native.length;
111b4f8e52bSBjorn Andersson 
112b4f8e52bSBjorn Andersson 	writel(tail, pipe->tail);
113b4f8e52bSBjorn Andersson }
114b4f8e52bSBjorn Andersson 
115e45c5dc2SBjorn Andersson static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe)
116e45c5dc2SBjorn Andersson {
117e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
118b4f8e52bSBjorn Andersson 	unsigned int head;
119b4f8e52bSBjorn Andersson 	unsigned int tail;
120b4f8e52bSBjorn Andersson 
121b4f8e52bSBjorn Andersson 	head = readl(pipe->head);
122b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
123b4f8e52bSBjorn Andersson 
124b4f8e52bSBjorn Andersson 	if (tail <= head)
125e45c5dc2SBjorn Andersson 		return pipe->native.length - head + tail;
126b4f8e52bSBjorn Andersson 	else
127b4f8e52bSBjorn Andersson 		return tail - head;
128b4f8e52bSBjorn Andersson }
129b4f8e52bSBjorn Andersson 
130e45c5dc2SBjorn Andersson static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe,
131b4f8e52bSBjorn Andersson 					   unsigned int head,
132b4f8e52bSBjorn Andersson 					   const void *data, size_t count)
133b4f8e52bSBjorn Andersson {
134b4f8e52bSBjorn Andersson 	size_t len;
135b4f8e52bSBjorn Andersson 
136e45c5dc2SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - head);
137b4f8e52bSBjorn Andersson 	if (len) {
138b4f8e52bSBjorn Andersson 		__iowrite32_copy(pipe->fifo + head, data,
139b4f8e52bSBjorn Andersson 				 len / sizeof(u32));
140b4f8e52bSBjorn Andersson 	}
141b4f8e52bSBjorn Andersson 
142b4f8e52bSBjorn Andersson 	if (len != count) {
143b4f8e52bSBjorn Andersson 		__iowrite32_copy(pipe->fifo, data + len,
144b4f8e52bSBjorn Andersson 				 (count - len) / sizeof(u32));
145b4f8e52bSBjorn Andersson 	}
146b4f8e52bSBjorn Andersson 
147b4f8e52bSBjorn Andersson 	head += count;
148e45c5dc2SBjorn Andersson 	if (head >= pipe->native.length)
149e45c5dc2SBjorn Andersson 		head -= pipe->native.length;
150b4f8e52bSBjorn Andersson 
151b4f8e52bSBjorn Andersson 	return head;
152b4f8e52bSBjorn Andersson }
153b4f8e52bSBjorn Andersson 
154e45c5dc2SBjorn Andersson static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe,
155e45c5dc2SBjorn Andersson 			       const void *hdr, size_t hlen,
156e45c5dc2SBjorn Andersson 			       const void *data, size_t dlen)
157e45c5dc2SBjorn Andersson {
158e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
159e45c5dc2SBjorn Andersson 	unsigned int head;
160e45c5dc2SBjorn Andersson 
161e45c5dc2SBjorn Andersson 	head = readl(pipe->head);
162e45c5dc2SBjorn Andersson 	head = glink_rpm_tx_write_one(pipe, head, hdr, hlen);
163e45c5dc2SBjorn Andersson 	head = glink_rpm_tx_write_one(pipe, head, data, dlen);
164e45c5dc2SBjorn Andersson 	writel(head, pipe->head);
165e45c5dc2SBjorn Andersson }
166e45c5dc2SBjorn Andersson 
167b4f8e52bSBjorn Andersson static int glink_rpm_parse_toc(struct device *dev,
168b4f8e52bSBjorn Andersson 			       void __iomem *msg_ram,
169b4f8e52bSBjorn Andersson 			       size_t msg_ram_size,
170b4f8e52bSBjorn Andersson 			       struct glink_rpm_pipe *rx,
171b4f8e52bSBjorn Andersson 			       struct glink_rpm_pipe *tx)
172b4f8e52bSBjorn Andersson {
173b4f8e52bSBjorn Andersson 	struct rpm_toc *toc;
174b4f8e52bSBjorn Andersson 	int num_entries;
175b4f8e52bSBjorn Andersson 	unsigned int id;
176b4f8e52bSBjorn Andersson 	size_t offset;
177b4f8e52bSBjorn Andersson 	size_t size;
178b4f8e52bSBjorn Andersson 	void *buf;
179b4f8e52bSBjorn Andersson 	int i;
180b4f8e52bSBjorn Andersson 
181b4f8e52bSBjorn Andersson 	buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL);
182b4f8e52bSBjorn Andersson 	if (!buf)
183b4f8e52bSBjorn Andersson 		return -ENOMEM;
184b4f8e52bSBjorn Andersson 
185b4f8e52bSBjorn Andersson 	__ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE,
186b4f8e52bSBjorn Andersson 			RPM_TOC_SIZE / sizeof(u32));
187b4f8e52bSBjorn Andersson 
188b4f8e52bSBjorn Andersson 	toc = buf;
189b4f8e52bSBjorn Andersson 
190b4f8e52bSBjorn Andersson 	if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) {
191b4f8e52bSBjorn Andersson 		dev_err(dev, "RPM TOC has invalid magic\n");
192b4f8e52bSBjorn Andersson 		goto err_inval;
193b4f8e52bSBjorn Andersson 	}
194b4f8e52bSBjorn Andersson 
195b4f8e52bSBjorn Andersson 	num_entries = le32_to_cpu(toc->count);
196b4f8e52bSBjorn Andersson 	if (num_entries > RPM_TOC_MAX_ENTRIES) {
197b4f8e52bSBjorn Andersson 		dev_err(dev, "Invalid number of toc entries\n");
198b4f8e52bSBjorn Andersson 		goto err_inval;
199b4f8e52bSBjorn Andersson 	}
200b4f8e52bSBjorn Andersson 
201b4f8e52bSBjorn Andersson 	for (i = 0; i < num_entries; i++) {
202b4f8e52bSBjorn Andersson 		id = le32_to_cpu(toc->entries[i].id);
203b4f8e52bSBjorn Andersson 		offset = le32_to_cpu(toc->entries[i].offset);
204b4f8e52bSBjorn Andersson 		size = le32_to_cpu(toc->entries[i].size);
205b4f8e52bSBjorn Andersson 
206b4f8e52bSBjorn Andersson 		if (offset > msg_ram_size || offset + size > msg_ram_size) {
207b4f8e52bSBjorn Andersson 			dev_err(dev, "TOC entry with invalid size\n");
208b4f8e52bSBjorn Andersson 			continue;
209b4f8e52bSBjorn Andersson 		}
210b4f8e52bSBjorn Andersson 
211b4f8e52bSBjorn Andersson 		switch (id) {
212b4f8e52bSBjorn Andersson 		case RPM_RX_FIFO_ID:
213e45c5dc2SBjorn Andersson 			rx->native.length = size;
214b4f8e52bSBjorn Andersson 
215b4f8e52bSBjorn Andersson 			rx->tail = msg_ram + offset;
216b4f8e52bSBjorn Andersson 			rx->head = msg_ram + offset + sizeof(u32);
217b4f8e52bSBjorn Andersson 			rx->fifo = msg_ram + offset + 2 * sizeof(u32);
218b4f8e52bSBjorn Andersson 			break;
219b4f8e52bSBjorn Andersson 		case RPM_TX_FIFO_ID:
220e45c5dc2SBjorn Andersson 			tx->native.length = size;
221b4f8e52bSBjorn Andersson 
222b4f8e52bSBjorn Andersson 			tx->tail = msg_ram + offset;
223b4f8e52bSBjorn Andersson 			tx->head = msg_ram + offset + sizeof(u32);
224b4f8e52bSBjorn Andersson 			tx->fifo = msg_ram + offset + 2 * sizeof(u32);
225b4f8e52bSBjorn Andersson 			break;
226b4f8e52bSBjorn Andersson 		}
227b4f8e52bSBjorn Andersson 	}
228b4f8e52bSBjorn Andersson 
229b4f8e52bSBjorn Andersson 	if (!rx->fifo || !tx->fifo) {
230b4f8e52bSBjorn Andersson 		dev_err(dev, "Unable to find rx and tx descriptors\n");
231b4f8e52bSBjorn Andersson 		goto err_inval;
232b4f8e52bSBjorn Andersson 	}
233b4f8e52bSBjorn Andersson 
234b4f8e52bSBjorn Andersson 	kfree(buf);
235b4f8e52bSBjorn Andersson 	return 0;
236b4f8e52bSBjorn Andersson 
237b4f8e52bSBjorn Andersson err_inval:
238b4f8e52bSBjorn Andersson 	kfree(buf);
239b4f8e52bSBjorn Andersson 	return -EINVAL;
240b4f8e52bSBjorn Andersson }
241b4f8e52bSBjorn Andersson 
2426799c434SBjorn Andersson static int glink_rpm_probe(struct platform_device *pdev)
2436799c434SBjorn Andersson {
2446799c434SBjorn Andersson 	struct qcom_glink *glink;
2456799c434SBjorn Andersson 	struct glink_rpm_pipe *rx_pipe;
2466799c434SBjorn Andersson 	struct glink_rpm_pipe *tx_pipe;
2476799c434SBjorn Andersson 	struct device_node *np;
2486799c434SBjorn Andersson 	void __iomem *msg_ram;
2496799c434SBjorn Andersson 	size_t msg_ram_size;
2506799c434SBjorn Andersson 	struct device *dev = &pdev->dev;
2516799c434SBjorn Andersson 	struct resource r;
2526799c434SBjorn Andersson 	int ret;
2536799c434SBjorn Andersson 
2546799c434SBjorn Andersson 	rx_pipe = devm_kzalloc(&pdev->dev, sizeof(*rx_pipe), GFP_KERNEL);
2556799c434SBjorn Andersson 	tx_pipe = devm_kzalloc(&pdev->dev, sizeof(*tx_pipe), GFP_KERNEL);
2566799c434SBjorn Andersson 	if (!rx_pipe || !tx_pipe)
2576799c434SBjorn Andersson 		return -ENOMEM;
2586799c434SBjorn Andersson 
259b4f8e52bSBjorn Andersson 	np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0);
260b4f8e52bSBjorn Andersson 	ret = of_address_to_resource(np, 0, &r);
261b4f8e52bSBjorn Andersson 	of_node_put(np);
262b4f8e52bSBjorn Andersson 	if (ret)
263b4f8e52bSBjorn Andersson 		return ret;
264b4f8e52bSBjorn Andersson 
265b4f8e52bSBjorn Andersson 	msg_ram = devm_ioremap(dev, r.start, resource_size(&r));
266b4f8e52bSBjorn Andersson 	msg_ram_size = resource_size(&r);
267b4f8e52bSBjorn Andersson 	if (!msg_ram)
268b4f8e52bSBjorn Andersson 		return -ENOMEM;
269b4f8e52bSBjorn Andersson 
270b4f8e52bSBjorn Andersson 	ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size,
271e45c5dc2SBjorn Andersson 				  rx_pipe, tx_pipe);
272b4f8e52bSBjorn Andersson 	if (ret)
273b4f8e52bSBjorn Andersson 		return ret;
274b4f8e52bSBjorn Andersson 
275e45c5dc2SBjorn Andersson 	/* Pipe specific accessors */
276e45c5dc2SBjorn Andersson 	rx_pipe->native.avail = glink_rpm_rx_avail;
277e45c5dc2SBjorn Andersson 	rx_pipe->native.peak = glink_rpm_rx_peak;
278e45c5dc2SBjorn Andersson 	rx_pipe->native.advance = glink_rpm_rx_advance;
279e45c5dc2SBjorn Andersson 	tx_pipe->native.avail = glink_rpm_tx_avail;
280e45c5dc2SBjorn Andersson 	tx_pipe->native.write = glink_rpm_tx_write;
281e45c5dc2SBjorn Andersson 
282e45c5dc2SBjorn Andersson 	writel(0, tx_pipe->head);
283e45c5dc2SBjorn Andersson 	writel(0, rx_pipe->tail);
284b4f8e52bSBjorn Andersson 
2856799c434SBjorn Andersson 	glink = qcom_glink_native_probe(&pdev->dev, &rx_pipe->native,
2866799c434SBjorn Andersson 					&tx_pipe->native);
2876799c434SBjorn Andersson 	if (IS_ERR(glink))
2886799c434SBjorn Andersson 		return PTR_ERR(glink);
289b4f8e52bSBjorn Andersson 
290b4f8e52bSBjorn Andersson 	platform_set_drvdata(pdev, glink);
291b4f8e52bSBjorn Andersson 
292b4f8e52bSBjorn Andersson 	return 0;
293b4f8e52bSBjorn Andersson }
294b4f8e52bSBjorn Andersson 
295b4f8e52bSBjorn Andersson static int glink_rpm_remove(struct platform_device *pdev)
296b4f8e52bSBjorn Andersson {
297d7101febSBjorn Andersson 	struct qcom_glink *glink = platform_get_drvdata(pdev);
298b4f8e52bSBjorn Andersson 
299835764ddSBjorn Andersson 	qcom_glink_native_remove(glink);
300b4f8e52bSBjorn Andersson 
301b4f8e52bSBjorn Andersson 	return 0;
302b4f8e52bSBjorn Andersson }
303b4f8e52bSBjorn Andersson 
304b4f8e52bSBjorn Andersson static const struct of_device_id glink_rpm_of_match[] = {
305b4f8e52bSBjorn Andersson 	{ .compatible = "qcom,glink-rpm" },
306b4f8e52bSBjorn Andersson 	{}
307b4f8e52bSBjorn Andersson };
308b4f8e52bSBjorn Andersson MODULE_DEVICE_TABLE(of, glink_rpm_of_match);
309b4f8e52bSBjorn Andersson 
310b4f8e52bSBjorn Andersson static struct platform_driver glink_rpm_driver = {
311b4f8e52bSBjorn Andersson 	.probe = glink_rpm_probe,
312b4f8e52bSBjorn Andersson 	.remove = glink_rpm_remove,
313b4f8e52bSBjorn Andersson 	.driver = {
314b4f8e52bSBjorn Andersson 		.name = "qcom_glink_rpm",
315b4f8e52bSBjorn Andersson 		.of_match_table = glink_rpm_of_match,
316b4f8e52bSBjorn Andersson 	},
317b4f8e52bSBjorn Andersson };
318b4f8e52bSBjorn Andersson 
319b4f8e52bSBjorn Andersson static int __init glink_rpm_init(void)
320b4f8e52bSBjorn Andersson {
321b4f8e52bSBjorn Andersson 	return platform_driver_register(&glink_rpm_driver);
322b4f8e52bSBjorn Andersson }
323b4f8e52bSBjorn Andersson subsys_initcall(glink_rpm_init);
324b4f8e52bSBjorn Andersson 
325b4f8e52bSBjorn Andersson static void __exit glink_rpm_exit(void)
326b4f8e52bSBjorn Andersson {
327b4f8e52bSBjorn Andersson 	platform_driver_unregister(&glink_rpm_driver);
328b4f8e52bSBjorn Andersson }
329b4f8e52bSBjorn Andersson module_exit(glink_rpm_exit);
330b4f8e52bSBjorn Andersson 
331b4f8e52bSBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
332b4f8e52bSBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK RPM driver");
333b4f8e52bSBjorn Andersson MODULE_LICENSE("GPL v2");
334