184369fbeSSuman Anna // SPDX-License-Identifier: GPL-2.0
2b4f8e52bSBjorn Andersson /*
3b4f8e52bSBjorn Andersson  * Copyright (c) 2016-2017, Linaro Ltd
4b4f8e52bSBjorn Andersson  */
5b4f8e52bSBjorn Andersson 
6b4f8e52bSBjorn Andersson #include <linux/idr.h>
7b4f8e52bSBjorn Andersson #include <linux/interrupt.h>
8b4f8e52bSBjorn Andersson #include <linux/io.h>
9b4f8e52bSBjorn Andersson #include <linux/list.h>
10b4f8e52bSBjorn Andersson #include <linux/mfd/syscon.h>
11b4f8e52bSBjorn Andersson #include <linux/module.h>
12b4f8e52bSBjorn Andersson #include <linux/of.h>
13b4f8e52bSBjorn Andersson #include <linux/of_address.h>
14f424d1cbSBjorn Andersson #include <linux/of_irq.h>
15b4f8e52bSBjorn Andersson #include <linux/platform_device.h>
16b4f8e52bSBjorn Andersson #include <linux/regmap.h>
17b4f8e52bSBjorn Andersson #include <linux/rpmsg.h>
18b4f8e52bSBjorn Andersson #include <linux/slab.h>
19b4f8e52bSBjorn Andersson #include <linux/workqueue.h>
20b4f8e52bSBjorn Andersson #include <linux/mailbox_client.h>
21b4f8e52bSBjorn Andersson 
22b4f8e52bSBjorn Andersson #include "rpmsg_internal.h"
23835764ddSBjorn Andersson #include "qcom_glink_native.h"
24b4f8e52bSBjorn Andersson 
25b4f8e52bSBjorn Andersson #define RPM_TOC_SIZE		256
26b4f8e52bSBjorn Andersson #define RPM_TOC_MAGIC		0x67727430 /* grt0 */
27b4f8e52bSBjorn Andersson #define RPM_TOC_MAX_ENTRIES	((RPM_TOC_SIZE - sizeof(struct rpm_toc)) / \
28b4f8e52bSBjorn Andersson 				 sizeof(struct rpm_toc_entry))
29b4f8e52bSBjorn Andersson 
30b4f8e52bSBjorn Andersson #define RPM_TX_FIFO_ID		0x61703272 /* ap2r */
31b4f8e52bSBjorn Andersson #define RPM_RX_FIFO_ID		0x72326170 /* r2ap */
32b4f8e52bSBjorn Andersson 
33e45c5dc2SBjorn Andersson #define to_rpm_pipe(p) container_of(p, struct glink_rpm_pipe, native)
34e45c5dc2SBjorn Andersson 
35b4f8e52bSBjorn Andersson struct rpm_toc_entry {
36b4f8e52bSBjorn Andersson 	__le32 id;
37b4f8e52bSBjorn Andersson 	__le32 offset;
38b4f8e52bSBjorn Andersson 	__le32 size;
39b4f8e52bSBjorn Andersson } __packed;
40b4f8e52bSBjorn Andersson 
41b4f8e52bSBjorn Andersson struct rpm_toc {
42b4f8e52bSBjorn Andersson 	__le32 magic;
43b4f8e52bSBjorn Andersson 	__le32 count;
44b4f8e52bSBjorn Andersson 
45b4f8e52bSBjorn Andersson 	struct rpm_toc_entry entries[];
46b4f8e52bSBjorn Andersson } __packed;
47b4f8e52bSBjorn Andersson 
48b4f8e52bSBjorn Andersson struct glink_rpm_pipe {
49e45c5dc2SBjorn Andersson 	struct qcom_glink_pipe native;
50e45c5dc2SBjorn Andersson 
51b4f8e52bSBjorn Andersson 	void __iomem *tail;
52b4f8e52bSBjorn Andersson 	void __iomem *head;
53b4f8e52bSBjorn Andersson 
54b4f8e52bSBjorn Andersson 	void __iomem *fifo;
55b4f8e52bSBjorn Andersson };
56b4f8e52bSBjorn Andersson 
57178c3af4SBjorn Andersson struct glink_rpm {
58178c3af4SBjorn Andersson 	struct qcom_glink *glink;
59178c3af4SBjorn Andersson 
60f424d1cbSBjorn Andersson 	int irq;
61f424d1cbSBjorn Andersson 
62f424d1cbSBjorn Andersson 	struct mbox_client mbox_client;
63f424d1cbSBjorn Andersson 	struct mbox_chan *mbox_chan;
64f424d1cbSBjorn Andersson 
65178c3af4SBjorn Andersson 	struct glink_rpm_pipe rx_pipe;
66178c3af4SBjorn Andersson 	struct glink_rpm_pipe tx_pipe;
67178c3af4SBjorn Andersson };
68178c3af4SBjorn Andersson 
glink_rpm_rx_avail(struct qcom_glink_pipe * glink_pipe)69e45c5dc2SBjorn Andersson static size_t glink_rpm_rx_avail(struct qcom_glink_pipe *glink_pipe)
70b4f8e52bSBjorn Andersson {
71e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
72b4f8e52bSBjorn Andersson 	unsigned int head;
73b4f8e52bSBjorn Andersson 	unsigned int tail;
74b4f8e52bSBjorn Andersson 
75b4f8e52bSBjorn Andersson 	head = readl(pipe->head);
76b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
77b4f8e52bSBjorn Andersson 
78b4f8e52bSBjorn Andersson 	if (head < tail)
79e45c5dc2SBjorn Andersson 		return pipe->native.length - tail + head;
80b4f8e52bSBjorn Andersson 	else
81b4f8e52bSBjorn Andersson 		return head - tail;
82b4f8e52bSBjorn Andersson }
83b4f8e52bSBjorn Andersson 
glink_rpm_rx_peek(struct qcom_glink_pipe * glink_pipe,void * data,unsigned int offset,size_t count)84a8f500c6SBjorn Andersson static void glink_rpm_rx_peek(struct qcom_glink_pipe *glink_pipe,
85b88eee97SBjorn Andersson 			      void *data, unsigned int offset, size_t count)
86b4f8e52bSBjorn Andersson {
87e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
88b4f8e52bSBjorn Andersson 	unsigned int tail;
89b4f8e52bSBjorn Andersson 	size_t len;
90b4f8e52bSBjorn Andersson 
91b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
92b88eee97SBjorn Andersson 	tail += offset;
93b88eee97SBjorn Andersson 	if (tail >= pipe->native.length)
94b88eee97SBjorn Andersson 		tail -= pipe->native.length;
95b4f8e52bSBjorn Andersson 
96e45c5dc2SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - tail);
97b4f8e52bSBjorn Andersson 	if (len) {
98b4f8e52bSBjorn Andersson 		__ioread32_copy(data, pipe->fifo + tail,
99b4f8e52bSBjorn Andersson 				len / sizeof(u32));
100b4f8e52bSBjorn Andersson 	}
101b4f8e52bSBjorn Andersson 
102b4f8e52bSBjorn Andersson 	if (len != count) {
103b4f8e52bSBjorn Andersson 		__ioread32_copy(data + len, pipe->fifo,
104b4f8e52bSBjorn Andersson 				(count - len) / sizeof(u32));
105b4f8e52bSBjorn Andersson 	}
106b4f8e52bSBjorn Andersson }
107b4f8e52bSBjorn Andersson 
glink_rpm_rx_advance(struct qcom_glink_pipe * glink_pipe,size_t count)108e45c5dc2SBjorn Andersson static void glink_rpm_rx_advance(struct qcom_glink_pipe *glink_pipe,
109b4f8e52bSBjorn Andersson 				 size_t count)
110b4f8e52bSBjorn Andersson {
111e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
112b4f8e52bSBjorn Andersson 	unsigned int tail;
113b4f8e52bSBjorn Andersson 
114b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
115b4f8e52bSBjorn Andersson 
116b4f8e52bSBjorn Andersson 	tail += count;
117e45c5dc2SBjorn Andersson 	if (tail >= pipe->native.length)
118e45c5dc2SBjorn Andersson 		tail -= pipe->native.length;
119b4f8e52bSBjorn Andersson 
120b4f8e52bSBjorn Andersson 	writel(tail, pipe->tail);
121b4f8e52bSBjorn Andersson }
122b4f8e52bSBjorn Andersson 
glink_rpm_tx_avail(struct qcom_glink_pipe * glink_pipe)123e45c5dc2SBjorn Andersson static size_t glink_rpm_tx_avail(struct qcom_glink_pipe *glink_pipe)
124e45c5dc2SBjorn Andersson {
125e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
126b4f8e52bSBjorn Andersson 	unsigned int head;
127b4f8e52bSBjorn Andersson 	unsigned int tail;
128b4f8e52bSBjorn Andersson 
129b4f8e52bSBjorn Andersson 	head = readl(pipe->head);
130b4f8e52bSBjorn Andersson 	tail = readl(pipe->tail);
131b4f8e52bSBjorn Andersson 
132b4f8e52bSBjorn Andersson 	if (tail <= head)
133e45c5dc2SBjorn Andersson 		return pipe->native.length - head + tail;
134b4f8e52bSBjorn Andersson 	else
135b4f8e52bSBjorn Andersson 		return tail - head;
136b4f8e52bSBjorn Andersson }
137b4f8e52bSBjorn Andersson 
glink_rpm_tx_write_one(struct glink_rpm_pipe * pipe,unsigned int head,const void * data,size_t count)138e45c5dc2SBjorn Andersson static unsigned int glink_rpm_tx_write_one(struct glink_rpm_pipe *pipe,
139b4f8e52bSBjorn Andersson 					   unsigned int head,
140b4f8e52bSBjorn Andersson 					   const void *data, size_t count)
141b4f8e52bSBjorn Andersson {
142b4f8e52bSBjorn Andersson 	size_t len;
143b4f8e52bSBjorn Andersson 
144e45c5dc2SBjorn Andersson 	len = min_t(size_t, count, pipe->native.length - head);
145b4f8e52bSBjorn Andersson 	if (len) {
146b4f8e52bSBjorn Andersson 		__iowrite32_copy(pipe->fifo + head, data,
147b4f8e52bSBjorn Andersson 				 len / sizeof(u32));
148b4f8e52bSBjorn Andersson 	}
149b4f8e52bSBjorn Andersson 
150b4f8e52bSBjorn Andersson 	if (len != count) {
151b4f8e52bSBjorn Andersson 		__iowrite32_copy(pipe->fifo, data + len,
152b4f8e52bSBjorn Andersson 				 (count - len) / sizeof(u32));
153b4f8e52bSBjorn Andersson 	}
154b4f8e52bSBjorn Andersson 
155b4f8e52bSBjorn Andersson 	head += count;
156e45c5dc2SBjorn Andersson 	if (head >= pipe->native.length)
157e45c5dc2SBjorn Andersson 		head -= pipe->native.length;
158b4f8e52bSBjorn Andersson 
159b4f8e52bSBjorn Andersson 	return head;
160b4f8e52bSBjorn Andersson }
161b4f8e52bSBjorn Andersson 
glink_rpm_tx_write(struct qcom_glink_pipe * glink_pipe,const void * hdr,size_t hlen,const void * data,size_t dlen)162e45c5dc2SBjorn Andersson static void glink_rpm_tx_write(struct qcom_glink_pipe *glink_pipe,
163e45c5dc2SBjorn Andersson 			       const void *hdr, size_t hlen,
164e45c5dc2SBjorn Andersson 			       const void *data, size_t dlen)
165e45c5dc2SBjorn Andersson {
166e45c5dc2SBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
1677339859dSBjorn Andersson 	size_t tlen = hlen + dlen;
1687339859dSBjorn Andersson 	size_t aligned_dlen;
169e45c5dc2SBjorn Andersson 	unsigned int head;
1707339859dSBjorn Andersson 	char padding[8] = {0};
1717339859dSBjorn Andersson 	size_t pad;
1727339859dSBjorn Andersson 
1737339859dSBjorn Andersson 	/* Header length comes from glink native and is always 4 byte aligned */
1747339859dSBjorn Andersson 	if (WARN(hlen % 4, "Glink Header length must be 4 bytes aligned\n"))
1757339859dSBjorn Andersson 		return;
1767339859dSBjorn Andersson 
1777339859dSBjorn Andersson 	/*
1787339859dSBjorn Andersson 	 * Move the unaligned tail of the message to the padding chunk, to
1797339859dSBjorn Andersson 	 * ensure word aligned accesses
1807339859dSBjorn Andersson 	 */
1817339859dSBjorn Andersson 	aligned_dlen = ALIGN_DOWN(dlen, 4);
1827339859dSBjorn Andersson 	if (aligned_dlen != dlen)
1837339859dSBjorn Andersson 		memcpy(padding, data + aligned_dlen, dlen - aligned_dlen);
184e45c5dc2SBjorn Andersson 
185e45c5dc2SBjorn Andersson 	head = readl(pipe->head);
186e45c5dc2SBjorn Andersson 	head = glink_rpm_tx_write_one(pipe, head, hdr, hlen);
1877339859dSBjorn Andersson 	head = glink_rpm_tx_write_one(pipe, head, data, aligned_dlen);
1887339859dSBjorn Andersson 
1897339859dSBjorn Andersson 	pad = ALIGN(tlen, 8) - ALIGN_DOWN(tlen, 4);
1907339859dSBjorn Andersson 	if (pad)
1917339859dSBjorn Andersson 		head = glink_rpm_tx_write_one(pipe, head, padding, pad);
192e45c5dc2SBjorn Andersson 	writel(head, pipe->head);
193e45c5dc2SBjorn Andersson }
194e45c5dc2SBjorn Andersson 
glink_rpm_tx_kick(struct qcom_glink_pipe * glink_pipe)195f424d1cbSBjorn Andersson static void glink_rpm_tx_kick(struct qcom_glink_pipe *glink_pipe)
196f424d1cbSBjorn Andersson {
197f424d1cbSBjorn Andersson 	struct glink_rpm_pipe *pipe = to_rpm_pipe(glink_pipe);
198f424d1cbSBjorn Andersson 	struct glink_rpm *rpm = container_of(pipe, struct glink_rpm, tx_pipe);
199f424d1cbSBjorn Andersson 
200f424d1cbSBjorn Andersson 	mbox_send_message(rpm->mbox_chan, NULL);
201f424d1cbSBjorn Andersson 	mbox_client_txdone(rpm->mbox_chan, 0);
202f424d1cbSBjorn Andersson }
203f424d1cbSBjorn Andersson 
qcom_glink_rpm_intr(int irq,void * data)204f424d1cbSBjorn Andersson static irqreturn_t qcom_glink_rpm_intr(int irq, void *data)
205f424d1cbSBjorn Andersson {
206f424d1cbSBjorn Andersson 	struct glink_rpm *rpm = data;
207f424d1cbSBjorn Andersson 
208f424d1cbSBjorn Andersson 	qcom_glink_native_rx(rpm->glink);
209f424d1cbSBjorn Andersson 
210f424d1cbSBjorn Andersson 	return IRQ_HANDLED;
211f424d1cbSBjorn Andersson }
212f424d1cbSBjorn Andersson 
glink_rpm_parse_toc(struct device * dev,void __iomem * msg_ram,size_t msg_ram_size,struct glink_rpm_pipe * rx,struct glink_rpm_pipe * tx)213b4f8e52bSBjorn Andersson static int glink_rpm_parse_toc(struct device *dev,
214b4f8e52bSBjorn Andersson 			       void __iomem *msg_ram,
215b4f8e52bSBjorn Andersson 			       size_t msg_ram_size,
216b4f8e52bSBjorn Andersson 			       struct glink_rpm_pipe *rx,
217b4f8e52bSBjorn Andersson 			       struct glink_rpm_pipe *tx)
218b4f8e52bSBjorn Andersson {
219b4f8e52bSBjorn Andersson 	struct rpm_toc *toc;
220b4f8e52bSBjorn Andersson 	int num_entries;
221b4f8e52bSBjorn Andersson 	unsigned int id;
222b4f8e52bSBjorn Andersson 	size_t offset;
223b4f8e52bSBjorn Andersson 	size_t size;
224b4f8e52bSBjorn Andersson 	void *buf;
225b4f8e52bSBjorn Andersson 	int i;
226b4f8e52bSBjorn Andersson 
227b4f8e52bSBjorn Andersson 	buf = kzalloc(RPM_TOC_SIZE, GFP_KERNEL);
228b4f8e52bSBjorn Andersson 	if (!buf)
229b4f8e52bSBjorn Andersson 		return -ENOMEM;
230b4f8e52bSBjorn Andersson 
231b4f8e52bSBjorn Andersson 	__ioread32_copy(buf, msg_ram + msg_ram_size - RPM_TOC_SIZE,
232b4f8e52bSBjorn Andersson 			RPM_TOC_SIZE / sizeof(u32));
233b4f8e52bSBjorn Andersson 
234b4f8e52bSBjorn Andersson 	toc = buf;
235b4f8e52bSBjorn Andersson 
236b4f8e52bSBjorn Andersson 	if (le32_to_cpu(toc->magic) != RPM_TOC_MAGIC) {
237b4f8e52bSBjorn Andersson 		dev_err(dev, "RPM TOC has invalid magic\n");
238b4f8e52bSBjorn Andersson 		goto err_inval;
239b4f8e52bSBjorn Andersson 	}
240b4f8e52bSBjorn Andersson 
241b4f8e52bSBjorn Andersson 	num_entries = le32_to_cpu(toc->count);
242b4f8e52bSBjorn Andersson 	if (num_entries > RPM_TOC_MAX_ENTRIES) {
243b4f8e52bSBjorn Andersson 		dev_err(dev, "Invalid number of toc entries\n");
244b4f8e52bSBjorn Andersson 		goto err_inval;
245b4f8e52bSBjorn Andersson 	}
246b4f8e52bSBjorn Andersson 
247b4f8e52bSBjorn Andersson 	for (i = 0; i < num_entries; i++) {
248b4f8e52bSBjorn Andersson 		id = le32_to_cpu(toc->entries[i].id);
249b4f8e52bSBjorn Andersson 		offset = le32_to_cpu(toc->entries[i].offset);
250b4f8e52bSBjorn Andersson 		size = le32_to_cpu(toc->entries[i].size);
251b4f8e52bSBjorn Andersson 
252b4f8e52bSBjorn Andersson 		if (offset > msg_ram_size || offset + size > msg_ram_size) {
253b4f8e52bSBjorn Andersson 			dev_err(dev, "TOC entry with invalid size\n");
254b4f8e52bSBjorn Andersson 			continue;
255b4f8e52bSBjorn Andersson 		}
256b4f8e52bSBjorn Andersson 
257b4f8e52bSBjorn Andersson 		switch (id) {
258b4f8e52bSBjorn Andersson 		case RPM_RX_FIFO_ID:
259e45c5dc2SBjorn Andersson 			rx->native.length = size;
260b4f8e52bSBjorn Andersson 
261b4f8e52bSBjorn Andersson 			rx->tail = msg_ram + offset;
262b4f8e52bSBjorn Andersson 			rx->head = msg_ram + offset + sizeof(u32);
263b4f8e52bSBjorn Andersson 			rx->fifo = msg_ram + offset + 2 * sizeof(u32);
264b4f8e52bSBjorn Andersson 			break;
265b4f8e52bSBjorn Andersson 		case RPM_TX_FIFO_ID:
266e45c5dc2SBjorn Andersson 			tx->native.length = size;
267b4f8e52bSBjorn Andersson 
268b4f8e52bSBjorn Andersson 			tx->tail = msg_ram + offset;
269b4f8e52bSBjorn Andersson 			tx->head = msg_ram + offset + sizeof(u32);
270b4f8e52bSBjorn Andersson 			tx->fifo = msg_ram + offset + 2 * sizeof(u32);
271b4f8e52bSBjorn Andersson 			break;
272b4f8e52bSBjorn Andersson 		}
273b4f8e52bSBjorn Andersson 	}
274b4f8e52bSBjorn Andersson 
275b4f8e52bSBjorn Andersson 	if (!rx->fifo || !tx->fifo) {
276b4f8e52bSBjorn Andersson 		dev_err(dev, "Unable to find rx and tx descriptors\n");
277b4f8e52bSBjorn Andersson 		goto err_inval;
278b4f8e52bSBjorn Andersson 	}
279b4f8e52bSBjorn Andersson 
280b4f8e52bSBjorn Andersson 	kfree(buf);
281b4f8e52bSBjorn Andersson 	return 0;
282b4f8e52bSBjorn Andersson 
283b4f8e52bSBjorn Andersson err_inval:
284b4f8e52bSBjorn Andersson 	kfree(buf);
285b4f8e52bSBjorn Andersson 	return -EINVAL;
286b4f8e52bSBjorn Andersson }
287b4f8e52bSBjorn Andersson 
glink_rpm_probe(struct platform_device * pdev)2886799c434SBjorn Andersson static int glink_rpm_probe(struct platform_device *pdev)
2896799c434SBjorn Andersson {
2906799c434SBjorn Andersson 	struct qcom_glink *glink;
291178c3af4SBjorn Andersson 	struct glink_rpm *rpm;
2926799c434SBjorn Andersson 	struct device_node *np;
2936799c434SBjorn Andersson 	void __iomem *msg_ram;
2946799c434SBjorn Andersson 	size_t msg_ram_size;
2956799c434SBjorn Andersson 	struct device *dev = &pdev->dev;
2966799c434SBjorn Andersson 	struct resource r;
2976799c434SBjorn Andersson 	int ret;
2986799c434SBjorn Andersson 
299178c3af4SBjorn Andersson 	rpm = devm_kzalloc(&pdev->dev, sizeof(*rpm), GFP_KERNEL);
300178c3af4SBjorn Andersson 	if (!rpm)
3016799c434SBjorn Andersson 		return -ENOMEM;
3026799c434SBjorn Andersson 
303b4f8e52bSBjorn Andersson 	np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", 0);
304b4f8e52bSBjorn Andersson 	ret = of_address_to_resource(np, 0, &r);
305b4f8e52bSBjorn Andersson 	of_node_put(np);
306b4f8e52bSBjorn Andersson 	if (ret)
307b4f8e52bSBjorn Andersson 		return ret;
308b4f8e52bSBjorn Andersson 
309b4f8e52bSBjorn Andersson 	msg_ram = devm_ioremap(dev, r.start, resource_size(&r));
310b4f8e52bSBjorn Andersson 	msg_ram_size = resource_size(&r);
311b4f8e52bSBjorn Andersson 	if (!msg_ram)
312b4f8e52bSBjorn Andersson 		return -ENOMEM;
313b4f8e52bSBjorn Andersson 
314b4f8e52bSBjorn Andersson 	ret = glink_rpm_parse_toc(dev, msg_ram, msg_ram_size,
315178c3af4SBjorn Andersson 				  &rpm->rx_pipe, &rpm->tx_pipe);
316b4f8e52bSBjorn Andersson 	if (ret)
317b4f8e52bSBjorn Andersson 		return ret;
318b4f8e52bSBjorn Andersson 
319f424d1cbSBjorn Andersson 	rpm->irq = of_irq_get(dev->of_node, 0);
320f424d1cbSBjorn Andersson 	ret = devm_request_irq(dev, rpm->irq, qcom_glink_rpm_intr,
321f424d1cbSBjorn Andersson 			       IRQF_NO_SUSPEND | IRQF_NO_AUTOEN,
322f424d1cbSBjorn Andersson 			       "glink-rpm", rpm);
323f424d1cbSBjorn Andersson 	if (ret) {
324f424d1cbSBjorn Andersson 		dev_err(dev, "failed to request IRQ\n");
325f424d1cbSBjorn Andersson 		return ret;
326f424d1cbSBjorn Andersson 	}
327f424d1cbSBjorn Andersson 
328f424d1cbSBjorn Andersson 	rpm->mbox_client.dev = dev;
329f424d1cbSBjorn Andersson 	rpm->mbox_client.knows_txdone = true;
330f424d1cbSBjorn Andersson 	rpm->mbox_chan = mbox_request_channel(&rpm->mbox_client, 0);
331f424d1cbSBjorn Andersson 	if (IS_ERR(rpm->mbox_chan))
332f424d1cbSBjorn Andersson 		return dev_err_probe(dev, PTR_ERR(rpm->mbox_chan), "failed to acquire IPC channel\n");
333f424d1cbSBjorn Andersson 
334e45c5dc2SBjorn Andersson 	/* Pipe specific accessors */
335178c3af4SBjorn Andersson 	rpm->rx_pipe.native.avail = glink_rpm_rx_avail;
336a8f500c6SBjorn Andersson 	rpm->rx_pipe.native.peek = glink_rpm_rx_peek;
337178c3af4SBjorn Andersson 	rpm->rx_pipe.native.advance = glink_rpm_rx_advance;
338178c3af4SBjorn Andersson 	rpm->tx_pipe.native.avail = glink_rpm_tx_avail;
339178c3af4SBjorn Andersson 	rpm->tx_pipe.native.write = glink_rpm_tx_write;
340f424d1cbSBjorn Andersson 	rpm->tx_pipe.native.kick = glink_rpm_tx_kick;
341e45c5dc2SBjorn Andersson 
342178c3af4SBjorn Andersson 	writel(0, rpm->tx_pipe.head);
343178c3af4SBjorn Andersson 	writel(0, rpm->rx_pipe.tail);
344b4f8e52bSBjorn Andersson 
345178c3af4SBjorn Andersson 	glink = qcom_glink_native_probe(dev,
346d31ad615SSricharan R 					0,
347178c3af4SBjorn Andersson 					&rpm->rx_pipe.native,
348178c3af4SBjorn Andersson 					&rpm->tx_pipe.native,
349933b45daSSricharan R 					true);
350f424d1cbSBjorn Andersson 	if (IS_ERR(glink)) {
351f424d1cbSBjorn Andersson 		mbox_free_channel(rpm->mbox_chan);
3526799c434SBjorn Andersson 		return PTR_ERR(glink);
353f424d1cbSBjorn Andersson 	}
354b4f8e52bSBjorn Andersson 
355178c3af4SBjorn Andersson 	rpm->glink = glink;
356178c3af4SBjorn Andersson 
357178c3af4SBjorn Andersson 	platform_set_drvdata(pdev, rpm);
358b4f8e52bSBjorn Andersson 
359f424d1cbSBjorn Andersson 	enable_irq(rpm->irq);
360f424d1cbSBjorn Andersson 
361b4f8e52bSBjorn Andersson 	return 0;
362b4f8e52bSBjorn Andersson }
363b4f8e52bSBjorn Andersson 
glink_rpm_remove(struct platform_device * pdev)364*49446e57SUwe Kleine-König static void glink_rpm_remove(struct platform_device *pdev)
365b4f8e52bSBjorn Andersson {
366178c3af4SBjorn Andersson 	struct glink_rpm *rpm = platform_get_drvdata(pdev);
367178c3af4SBjorn Andersson 	struct qcom_glink *glink = rpm->glink;
368b4f8e52bSBjorn Andersson 
369f424d1cbSBjorn Andersson 	disable_irq(rpm->irq);
370f424d1cbSBjorn Andersson 
371835764ddSBjorn Andersson 	qcom_glink_native_remove(glink);
372b4f8e52bSBjorn Andersson 
373f424d1cbSBjorn Andersson 	mbox_free_channel(rpm->mbox_chan);
374b4f8e52bSBjorn Andersson }
375b4f8e52bSBjorn Andersson 
376b4f8e52bSBjorn Andersson static const struct of_device_id glink_rpm_of_match[] = {
377b4f8e52bSBjorn Andersson 	{ .compatible = "qcom,glink-rpm" },
378b4f8e52bSBjorn Andersson 	{}
379b4f8e52bSBjorn Andersson };
380b4f8e52bSBjorn Andersson MODULE_DEVICE_TABLE(of, glink_rpm_of_match);
381b4f8e52bSBjorn Andersson 
382b4f8e52bSBjorn Andersson static struct platform_driver glink_rpm_driver = {
383b4f8e52bSBjorn Andersson 	.probe = glink_rpm_probe,
384*49446e57SUwe Kleine-König 	.remove_new = glink_rpm_remove,
385b4f8e52bSBjorn Andersson 	.driver = {
386b4f8e52bSBjorn Andersson 		.name = "qcom_glink_rpm",
387b4f8e52bSBjorn Andersson 		.of_match_table = glink_rpm_of_match,
388b4f8e52bSBjorn Andersson 	},
389b4f8e52bSBjorn Andersson };
390b4f8e52bSBjorn Andersson 
glink_rpm_init(void)391b4f8e52bSBjorn Andersson static int __init glink_rpm_init(void)
392b4f8e52bSBjorn Andersson {
393b4f8e52bSBjorn Andersson 	return platform_driver_register(&glink_rpm_driver);
394b4f8e52bSBjorn Andersson }
395b4f8e52bSBjorn Andersson subsys_initcall(glink_rpm_init);
396b4f8e52bSBjorn Andersson 
glink_rpm_exit(void)397b4f8e52bSBjorn Andersson static void __exit glink_rpm_exit(void)
398b4f8e52bSBjorn Andersson {
399b4f8e52bSBjorn Andersson 	platform_driver_unregister(&glink_rpm_driver);
400b4f8e52bSBjorn Andersson }
401b4f8e52bSBjorn Andersson module_exit(glink_rpm_exit);
402b4f8e52bSBjorn Andersson 
403b4f8e52bSBjorn Andersson MODULE_AUTHOR("Bjorn Andersson <bjorn.andersson@linaro.org>");
404b4f8e52bSBjorn Andersson MODULE_DESCRIPTION("Qualcomm GLINK RPM driver");
405b4f8e52bSBjorn Andersson MODULE_LICENSE("GPL v2");
406